javascriptintegertracepurescriptunless

What do I wrong comparing a tuple of integers in PureScript?


Debugging my event handler for the mouse move event, I wrote this code:

 unless (scrollDelta == Tuple 0 0) $ trace (show scrollDelta) \_ -> do ...

And I get console output

Tuple 0 0

The idea behind this is to save a difference in the mouse position whenever the user moves the mouse with the first mouse button pressed.

Is this a compiler bug?

My workaround is to change the type of scrollDelta to Maybe (Tuple Int Int) where Nothing represents "no mouse movement while button pressed" instead of Tuple 0 0, which works.


Solution

  • Your problem is the difference between evaluation and execution.

    When you write something like:

    Console.log "foo"
    

    contrary to your intuition, that does not actually print anything to console. Instead, this creates an "action", which has to be "executed" at some later point, and it's only then that it will print something to the console.

    Conversely, same action can actually be "executed" several times, and thus produce multiple effects:

    printFoo = Console.log "foo"
    -- ^ Nothing is printed at this point
    
    main = do
        printFoo
        printFoo
        printFoo
        -- ^ "foo" is printed three times
    

    In the above example, evaluation happens once, but execution - three times.


    The way unless works, it takes a Boolean value and an "action", and it returns another "action":

    dontPrintFoo = unless true printFoo
    

    When you execute the action dontPrintFoo, it will be the action returned by unless, which will examine the Boolean parameter and choose not to execute the action printFoo.

    But say you have a bit more complicated scenario:

    dontPrintFoo = unless true $ if qux then printFoo else printBar
    

    Here, both parameters of unless must be "evaluated" (but not "executed"!) before they are passed to unless. This means that the value of qux and the result of if/then is calculated before calling unless. But the resulting action would be executed later, when the whole result is called from main.


    But trace is special. Magic. Even though it produces an effect (printing to console), the compiler thinks it's a pure function. Like 2 + 3 or like if qux then foo else bar. It's done this way for debugging convenience: so that you can insert tracing in pure, non-effectful code.

    This means that trace is evaluated at "evaluation" time, not "execution". Which means that it's evaluated before unless is even called, and therefore it's evaluated regardless of whether the Boolean parameter is true or false.


    If you want the tracing to only work when unless decided to execute the action, try traceM:

    unless (...) do
        traceM (show scrollDelta)
        foo
        bar