数据类型内部的惰性

问题描述 投票:0回答:1

我以为我很好地理解了惰性,直到我想出了下面的代码,它产生了一个

<<loop>>
错误。

weird = ([1],[1]) <> weird
main = print (head $ fst weird)

直观上,这就是我认为 Haskell 会做的事情: “我需要

weird
的第一个元素。而且我需要第一个元素的头。所以我需要计算
fst weird
。现在我知道来自半群实例实例的第一个
weird = [1] ++ fst weird
(或者我??) .所以太好了,我应该回来
1

我哪里弄错了?

haskell lazy-evaluation
1个回答
0
投票

这里是模式匹配出了问题。事实上,如果我们查看 2 元组实例 [src]

instance,我们会看到:

Semigroup
所以这里需要两个 2 元组,然后它将两者结合起来。但这意味着它
对第一个

第二个操作数进行模式匹配。对于第二个操作数,存在问题,因为这是计算的结果,因此会触发系统评估这些操作数。 匹配可能看起来没有必要,但可以传递

instance (Semigroup a, Semigroup b) => Semigroup (a, b) where (a,b) <> (a',b') = (a<>a',b<>b') stimes n (a,b) = (stimes n a, stimes n b)

或其他导致循环的机制,就像这里所做的那样,因此代码基本上要求检查第二个操作数是否是 2 元组。

我们可以做的是使用

无可辩驳的

模式,这样我们就假设数据构造函数保存并且仅在必要时才将其解压。所以我们可以自己实现某种求和: undefined

然后我们自己的实现适用于:

(<^>) :: (Semigroup a, Semigroup b) => (a, b) -> (a, b) -> (a, b) ~(a,b) <^> ~(a',b') = (a<>a',b<>b')

因此我们让实现变得更加懒惰以组合两个二元组。

我个人认为 2 元组(以及任何

n

元组)上

weird = ([1],[1]) <^> weird
main = print (head $ fst weird)
等的组合可以用无可辩驳的模式来完成。我不知道是否有充分的理由说明基础包中的情况并非如此。

© www.soinside.com 2019 - 2024. All rights reserved.