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.
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