这个ArrowLoop.loop定义是如何工作的?

23

ArrowLoop的函数实例包含:

loop :: ((b,d) -> (c,d)) -> (b -> c)
loop f b = let (c,d) = f (b,d) in c

首先,我有一个关于签名的问题:我们怎么可能从 (b,d) -> (c,d) 得到 b -> c?我的意思是,结果元组中的 c 可能取决于输入的两个元素,如何“切断” d 的影响?

其次,我不明白这里的 let 是如何工作的。 (c, d) = f(b, d) 不包含对 d 的循环定义吗? d 从哪里来?说实话,我很惊讶这是有效的语法,因为它看起来像是重新定义了 d

我的意思是,在数学上,这有一定的道理,例如 f 可以是一个复合函数,但我只提供实部 b,我需要选择虚部 d 的方式,使得当我计算 f(b,d) 时它不变,这将使它成为某种固定点。但是,如果这个类比成立,那么 let 表达式必须以某种方式“搜索”该固定点的 d(可能会有多个)。这对我来说看起来很神奇。还是我想得太复杂了?


4
第一个答案回答了你的问题。Haskell中的rec关键字是用来定义递归函数的。 - fuz
2个回答

14

这与标准的 fix 定义方式相同:

fix f = let x = f x in x

即,它以递归的方式fix完全相同的方式寻找一个固定点。

例如,作为一个微不足道的例子,考虑loop (\((),xs) -> (xs, 1:xs)) ()。这就像fix (\xs -> 1:xs)一样; 我们忽略我们的输入,并使用d输出(这里是xs)作为我们的主要输出。 loop具有的元组中的额外元素仅用于包含输入参数和输出值,因为箭头无法进行柯里化。考虑如何使用fix定义阶乘函数-您将结束使用柯里化,但在使用箭头时,您将使用loop给出的额外参数和输出。

基本上,loop打结了一个绳结,使得箭头可以访问其自身的辅助输出,就像fix打结了一个绳结,使得函数可以访问其自己的输出作为输入。


6
“寻找不动点” 正是 这段代码所做的事情。这展示了Haskell中的惰性计算。更多信息请参见维基百科

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接