Haskell中case表达式求值:为何匹配_时1´div´0不触发除零错误
问题涉及的Haskell代码
case 1 `div` 0 of _ -> 42
按常规求值逻辑直觉,这段代码会触发除零运行时错误,但实际测试运行会直接返回42,该现象和Haskell的惰性求值、模式匹配规则直接相关,以下是对应问题的解答:
1. 初始判断的准确性说明
判断的大方向正确,但可以补充更准确的底层逻辑:
这个表达式没有触发除零错误,确实是惰性求值导致的,但本质不是“通配符不检查待匹配内容”,而是Haskell的模式匹配遵循按需求值规则:只有当匹配过程必须获取待匹配表达式(即case后跟随的待检测值)的结构信息时,才会对表达式求值。
通配符_属于永远匹配成功的模式,不需要知道待匹配值的任何结构、内容信息,因此1 div 0这个表达式从头到尾没有被触发求值,自然不会抛出除零的运行时错误。
2. 无需计算待匹配表达式即可完成匹配的前提
只要匹配流程不需要获取待匹配表达式的任何结构信息,就不会触发求值,具体场景可以归纳为以下几类:
- 待匹配的首个生效分支是不可反驳模式,且不存在排在它前面的可反驳分支需要做匹配判断。不可反驳模式指匹配永远不会失败、不需要依赖待匹配值结构的模式,常见类型包括:
- 通配符模式
_:匹配任意值,不绑定任何变量,无任何信息需求 - 纯变量模式:比如直接写
x、res这类变量名,匹配任意值并将值绑定到对应变量,匹配阶段不需要结构信息。哪怕后续代码完全不使用这个绑定的变量,也不会触发求值,例如case 1div0 of x -> 42同样会直接返回42,不会触发除零错误 - 带惰性匹配标记
~的模式:在模式前加~后,匹配阶段会直接判定匹配成功,不会检查值的结构,只有当后续代码用到模式内绑定的变量时,才会进一步求值待匹配值的结构。例如case 1div0 of ~(a,b) -> 42运行时不会报错,只有在分支内引用a或b时才会触发除零错误
- 通配符模式
- 只要匹配流程中遇到需要判断的可反驳模式,就必须将待匹配表达式求值到弱头范式(即露出最外层构造器或字面量值)才能判断是否匹配,此时必然会触发计算。常见的可反驳模式包括:
- 构造器模式:比如
Just v、[]、x:xs、(a,b)这类,必须确认最外层构造器和模式一致才能匹配 - 字面量模式:比如
0、"haskell"、False这类,必须计算出具体值才能判断是否和模式相等 - 带严格匹配标记
!的模式:无论模式本身是什么,加了!标记后都会强制将待匹配值求值到弱头范式再进行匹配
- 构造器模式:比如
- 多分支场景的特殊注意点:如果case存在多个分支,哪怕最后一个分支是通配符这类不可反驳模式,只要前面存在可反驳模式,就会先求值待匹配表达式判断是否匹配前面的分支。例如
case 1div0 of [] -> 1; _ -> 42运行时依然会触发除零错误,因为第一个分支是可反驳的空列表构造器模式,必须先求值待匹配值才能判断是否匹配。
内容的提问来源于stack exchange,提问作者ExOster
相关产品推荐
相关产品推荐

