如果我说
let 5 = 10
,为什么5 + 1
返回的是6
而不是11
?let 5 = 10
,为什么5 + 1
返回的是6
而不是11
?当你说
let 5 = 10
这不是对5的重新定义,而是模式匹配,就像你说的那样。
foo 5 = undefined
... foo 10 ...
如果模式匹配失败,则该模式将无法匹配。
在let表达式中,匹配是惰性的。这意味着只有当由其绑定的变量被评估时才会进行匹配。这使我们能够编写以下内容:
let foo = undefined in 10
在你的表达式中,没有变量被绑定,所以此模式永远不会匹配。或许在let绑定中,没有任何变量的这种模式是毫无意义的,应该由编译器检测并报错,但语言本身并未禁止这种用法。let (5,x) = (10,True) in x
会提示错误信息 *** Exception: <interactive>:2:5-21: Irrefutable pattern failed for pattern (5, x)
。 - pigworkeronly5 xs = [5 | 5 <- xs]
是有意义的(如果你接受列表推导式可能失败的话)。 - effectfullylet (~5,x) = (10,True) in x
之所以返回 True
,仅仅是因为我们明确告诉编译器假设 5 模式匹配成功。 - rampionlet [] = [1,2] in True
- rampion基本上,
let 5 = 10 in ...
等同于
case 10 of ~5 -> ...
请注意~
符号,它标记了一个惰性模式或不可反驳模式。这是一个匹配任何内容的模式,并且将匹配推迟到某个变量被实际要求的时候。在模式5
中没有变量,因此什么也不会发生。
这种情况几乎没有用处,并且可以认为编译器应该在此处发出警告。
为了说明惰性模式的含义,请考虑以下内容:
case f 3 of
(x,y) -> g 10 x y
f 3
先被评估(到WHNF),暴露出对偶构造函数。然后,x,y
绑定到(尚未评估的)对组分量。最后,计算g 10
,将结果应用于x
(现在可能需要),然后应用于y
(可能导致要求x
或y
)。
相比之下,
case f 3 of
~(x,y) -> g 10 x y
不是从评估 f 3
开始。相反,x
绑定到未评估的 fst (f 3)
,y
绑定到未评估的 snd (f 3)
。我们改为从评估 g 10
开始。然后,将其应用于 x
:这可能会导致需要 x
,从而触发对 f 3
的评估。然后,将结果应用于 y
,引起类似的评估。大多数实现实际上会在 x
和 y
之间共享 f 3
的结果,以便最多计算一次。
catch m $ \ ~MyException -> ...
并不罕见,其中非绑定惰性模式匹配是提供异常类型的便捷方式。但在这种情况下使用数字字面量似乎有些牵强。 - dfeuer正如 @n.m. 所说,你正在进行模式匹配。以下是一些示例。
模式匹配可以成功
Prelude> let (a, 10) = (15, 10) in a
15
Prelude> let (a, 10) = (15, 15) in a
*** Exception: <interactive>:5:5-22: Irrefutable pattern failed for pattern (a, 10)
由于 Haskell 是惰性的,如果您不使用结果值,则代码将成功。这基本上就是您正在做的事情:
Prelude> let (a, 10) = (15, 15) in "Something else"
"Something else"
Prelude> let (a, 10, 999) = (15, 15) in "Something else"
<interactive>:7:20: error:
• Couldn't match expected type ‘(t, Integer, Integer)’
with actual type ‘(Integer, Integer)’
• In the expression: (15, 15)
In a pattern binding: (a, 10, 999) = (15, 15)
In the expression: let (a, 10, 999) = (15, 15) in "Something else"
• Relevant bindings include a :: t (bound at <interactive>:7:6)
let
绑定也会惰性匹配。case (15, 15) of { (a, 10) -> "Something else" }
会崩溃,而case (15, 15) of { ~(a, 10) -> "Something else" }
(由于惰性匹配,等同于您的第三个示例)会成功。 - duplodelet
和where
中使用惰性默认值是一个不好的主意,这也是感叹号模式有时会令人困惑的原因。 - dfeuerlet nats = [1..] in takeWhile isOkay nats
- Teodorlet
和where
中LHS上的模式,而不是简单的变量绑定。我认为let p = e1 in e2
应该与case e1 of p -> e2
完全相同,无论p
长什么样子。不幸的是,在Haskell中无法修复这个问题,但希望未来的语言会考虑按照我的方式处理它。 - dfeuer
5
是什么”。但是,我实际上对于写下let 5 = 10
竟然是可能的感到相当惊讶! - duplode+
进行“重载”来实现:let 1+1=3 in 1+1
;)。 - Random Devlet 5 = 10
中的5
仍然是一个模式(只是永远不会匹配),因此它不会绑定任何内容(就像let (x,5) = (6,6)
中的5
一样)。 - Random DevPrelude> :set -XBangPatterns
表示开启了 BangPatterns 扩展。然后Prelude> let !5 = 10
会产生异常*** Exception: <interactive>:9:5-11: Non-exhaustive patterns in pattern binding
,这是因为在模式匹配时没有覆盖到所有情况,而惰性计算隐藏了这个失败的匹配,导致你的误解得以继续存在。 - Thomas M. DuBuisson