您的第一个示例在地图中是严格的。下面查找print "1"
,然后运行它,程序实际打印 1。当然,这需要评估m
。
main = do let m = Map.fromList [(1, print "1")]
val <- m ! 1
return val
你可能打算写一些只读取地图的东西。以下不是严格的,因为val
没有在 case 表达式中使用。
main = do let m = Map.fromList [(1, print "1")]
let val = m ! 1
return val
您的第二个示例很严格,因为它检查结果是否lookup
成功以决定如何完成执行块。这需要阅读地图。它相当于:
do m <- ask
case lookup key m of
Nothing -> fail "oh noes"
Just x -> doSomething x
调试严格性问题
评估总是由 case 表达式或某些内置运算符(如+
整数)强制执行。如果你怀疑你的程序失败是因为一个值在它可用之前就被强制了,你会想知道哪个值被强制了,它在哪里被强制了。
哪个值是强制的?
在这种错误中,程序尝试评估取决于其自身评估结果的表达式。您可以使用trace
它来跟踪正在评估的表达式。在这个问题中,看起来 的值m
是被强制的,所以trace
在它被评估之前使用它来打印一条消息:
do m1 <- ask
let m = trace "Using m" m1
...
如果“Using m”是程序的最后一个输出(在 之前<<loop>>
),那么您离错误越来越近了。如果它不在输出中,那么m
就没有被评估,所以问题出在其他地方。如果输出中这行后面有内容,则程序继续执行,稍后发生错误,因此问题一定出在其他地方。
哪里被逼的?
这告诉您,在停止之前,评估至少达到了这一点。但它走了多远?问题真的发生在很久以后吗?要看到这一点,请尝试在trace
稍后评估的内容上添加一个。我们知道m
评估是为了决定maybe
运行的哪个分支,所以我们可以放在trace
这些点上。
do m1 <- ask
let m = trace "Using m" m1
maybe (trace "Used m" $ fail "oh noes")
(\x -> trace "Used m" $ doSomething x)
(lookup key m)
如果您在输出中看到“Using m”后跟“Used m”,那么您知道评估m
完成并且程序继续运行。如果您只看到“使用 m”,则程序在这些点之间停止。在这种特殊情况下,您不应看到“Used m”,因为会maybe
强制评估m
并导致<<loop>>
.