Operation that does Debug.log and returns input value

I don’t know if something like this already exists, but I found it helpful:

probeAs label x =
  Tuple.first ( x, Debug.log label (Debug.toString x) )

probe x = (probeAs "<<PROBE>>" x)

This lets you log an expression without having to break that expression out into some separate named thing.

probeAs creates a tuple in which the second argument is the call to the log function. Assuming Elm doesn’t optimize it out (it doesn’t appear to), then that should produce the output in the browser console. Only the first element is returned, which is the input.

probe is a one-argument specialization that doesn’t even make you provide a label, if you just want to do something very quickly.

Is there a better way to say Tuple.first (x, f x) … in particular one that is guaranteed not to optimize out f x ?

I may be missing something but how is this different from just using Debug.log directly? Debug.log already returns its argument, and implicitly calls Debug.toString on it to display it…seems to me the only difference is that probeAs will output a couple extra quotation marks since you’re explicitly converting to a string first :slightly_smiling_face:

Oops. I imagine what happened is that I assumed from reading about the existence of Debug.log that it would match the interface of console.log. So I tried giving it a string (which the signature said was the first parameter) and some steps later of mucking with it mutated it into what I expected…not accounting for the actual signature. :-/ I’ve been using it and only now am looking back at it, so good thing I did…

Okay, not so useful then. But the question part is still a question: Is there a better way to evaluate two expressions and take only one, with guarantee the expression you’re not returning won’t be optimized out?

What you mean by evaluating two expressions? Function itself is just a single expression. Sure it can evaluate more expression within itself but always as part of evaluating single expression. There is no imperative evaluating line 1, then evaluating line 2. What would be even point of evaluating one of the things and ignoring it’s result when language is pure?

That’s being out of the way there is this trick (since elm is strict):

f : Int -> Int
f v =
  let _ = Debug.log "I'm just always evaluated when this f is called" ()
  in v + 1

And you might be also interested checking elm alternatives to Haskell’s *> or <* like Maybe.Extra.next or Maybe.Extra.prev.

Debug.log has this type signature:

log : String -> a -> a

So it returns whatever you give it. Useful for logging all Msgs in an application:

update : Msg -> Model -> (Model, Cmd Msg)
update msg model = 
    case Debug.log "update" msg of
        ...

===

Another interesting one is Debug.todo which has this type signature:

todo : String -> a

Which is a bit weird, how does it know what a to return? Well, it does not return, it throws a runtime exception, which is why that unbound a can be tolerated.

You can use it like this:

someComplexFunction : Complex -> Type -> Signature -> YouGetTheIdea
someComplexFunction _ _ _ = 
    Debug.todo "My head hurts, I'll takle this one later..."

The type system is always able to bind that free a to whatever type you mocked up function will have. Lets you get a sketch of your code compiling quickly and easily, no matter how complex its type is.

1 Like

Yes… since there is no imperative evaluation then one would think an unused element would be optimized away.

In other words: there is not an “impure” annotation or anything like that to make this work. The reason the tuple works is only “on accident”…and Debug.log working at all in the first place is essentially “on accident”. There’s nothing special about it, and nothing you have to do (or can do) in particular special to use it differently (let’s imagine my example was meaningfully different).

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.