Haskell数据类型惰性引发<<loop>>错误的原因咨询
为什么这段Haskell代码会触发<>错误?
你的代码如下:
weird = ([1],[2]) <> weird main = print (head $ fst weird)
你的直观理解存在偏差,核心问题出在元组的Semigroup实例实现和递归绑定的求值逻辑上:
- 元组的
<>运算规则:对于(a,b) <> (c,d),Haskell会先对右侧的元组(c,d)进行模式匹配,提取出两个元素后,再分别对左右元组的对应元素执行<>运算,最终得到(a<>c, b<>d)。 - 你的代码中
weird = ([1],[2]) <> weird,等价于要求Haskell计算:weird = let (c,d) = weird in ([1]<>c, [2]<>d)。这时候求值器陷入了循环:- 要执行
<>运算,必须先把weird匹配成(c,d),也就是需要weird的顶层构造器是元组(,); - 但
weird本身的定义就是<>运算的结果,而<>运算的结果又是一个元组,这意味着必须先完成<>运算才能得到weird的构造器; - 两者互相依赖,导致求值器无法推进,最终触发
<<loop>>错误。
- 要执行
你以为的fst weird = [1] ++ fst weird之所以不成立,是因为这个推导的前提是fst (([1],[2]) <> weird) = [1] <> fst weird,但这个等式成立的前提是weird已经是一个能被匹配成元组的有效值——而你的递归定义里,weird根本无法完成这个匹配步骤,自然到不了列表拼接的阶段。
对比能正常运行的写法:
weird = (weirdFst, weirdSnd) where weirdFst = [1] ++ weirdFst weirdSnd = [2] ++ weirdSnd main = print (head $ fst weird)
这里weird直接由两个递归列表构造为元组,不需要依赖<>运算的模式匹配。当取fst weird时,直接得到weirdFst,而weirdFst = 1 : weirdFst,head可以直接取到第一个元素1,不需要求值整个递归列表。
内容的提问来源于stack exchange,提问作者141592653
相关产品推荐
相关产品推荐

