为什么 haskell 的 `fix` 似乎对元组有问题?

Why does haskell's `fix` seem to have trouble with tuples?

我正在努力研究定点和递归定义。

这个有效:

>>> take 10 $ let x = (0:x) in x
[0,0,0,0,0,0,0,0,0,0]

这做同样的事情,考虑到 fix:

的定义,这是有道理的
>>> take 10 $ fix (\x -> (0:x))
[0,0,0,0,0,0,0,0,0,0]

现在假设我开始摆弄递归定义的对:

>>> take 10 $ fst $ let (u,v) = (0:v,1:u) in (u,v)
[0,1,0,1,0,1,0,1,0,1]

好吧,我应该也可以用 fix 来写吧?

>>> take 10 $ fst $ fix (\(u,v) -> (0:v,1:u))
*** Exception: <<loop>>

但是没用。除非我做以下看似微不足道的改变:

>>> take 10 $ fst $ fix (\r -> let (u,v)=r in (0:v,1:u))
[0,1,0,1,0,1,0,1,0,1]

最后两个示例之间的关键区别是什么?

你想要

take 10 $ fst $ fix (\ ~(u,v) -> (0:v,1:u))
                      ^^^

从而使模式匹配变得懒惰。在 let 中,LHS 模式隐式为 lazy/irrefutable.

对于普通的 \(u,v) -> ...,在生成任何输出之前将需要 lambda 的参数——这使得函数对于 fix 来说过于严格。你需要的是

take 10 $ fst $ fix (\p -> (0:snd p,1:fst p))

这样参数就不会被 lambda 强制执行(那里没有匹配的构造函数)。惰性模式方法等同于上面的fst/snd