1

为鼠标移动事件调试我的事件处理程序,我编写了以下代码:

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

我得到控制台输出

Tuple 0 0

这背后的想法是在用户按下第一个鼠标按钮移动鼠标时保存鼠标位置的差异。

这是编译器错误吗?

我的解决方法是将 的类型更改为scrollDelta表示“按下按钮时没有鼠标移动”的地方,而不是Maybe (Tuple Int Int),这有效。NothingTuple 0 0

4

1 回答 1

4

您的问题是评估执行之间的区别。

当你写这样的东西时:

Console.log "foo"

与您的直觉相反,这实际上并没有向控制台打印任何内容。相反,这会创建一个“动作”,该动作必须在稍后的某个时间点“执行”,然后才会将某些内容打印到控制台。

相反,同一个动作实际上可以“执行”多次,从而产生多种效果:

printFoo = Console.log "foo"
-- ^ Nothing is printed at this point

main = do
    printFoo
    printFoo
    printFoo
    -- ^ "foo" is printed three times
  • “评价”正在创造“行动”
  • “执行”正在运行“动作”以产生它产生的任何效果

在上面的示例中,评估发生一次,但执行 - 三次。


工作方式unless,它接受一个Boolean值和一个“动作”,并返回另一个“动作”

dontPrintFoo = unless true printFoo

当您执行动作dontPrintFoo时,它将是由返回的动作unless,它将检查Boolean参数并选择执行动作printFoo

但是假设你有一个更复杂的场景:

dontPrintFoo = unless true $ if qux then printFoo else printBar

在这里, 的两个参数unless在传递给unless. 这意味着在调用之前计算/的值qux和结果。但是结果动作将在稍后执行,当整个结果从.ifthenunlessmain


但是trace很特别。魔法。即使它产生效果(打印到控制台),编译器仍认为它是一个纯函数。喜欢2 + 3或喜欢if qux then foo else bar。这样做是为了方便调试:以便您可以在纯的、无效的代码中插入跟踪。

这意味着trace在“评估”时评估,而不是“执行”。这意味着它甚至在被调用之前 unless就被评估过,因此无论Boolean参数是trueor ,它都会被评估false


如果您希望跟踪仅在unless决定执行操作时起作用,请尝试traceM

unless (...) do
    traceM (show scrollDelta)
    foo
    bar
于 2020-11-27T23:25:13.137 回答