loop
中Control.Arrow
的描述:
尽管运算仅发生一次,但循环运算符表示将输出值作为输入反馈的运算。它是箭头表示法的rec值递归构造的基础。
其源代码及其对(->)
的实例化:
class Arrow a => ArrowLoop a where
loop :: a (b,d) (c,d) -> a b c
instance ArrowLoop (->) where
loop f b = let (c,d) = f (b,d) in c
这立即使我想起了定点组合器fix
:
fix :: (a -> a) -> a
fix f = let x = f x in x
所以我的问题是:
loop
来实现特定的fix
?答案 0 :(得分:8)
当然可以。每个递归定义都可以用fix
编写:
loop f b = let (c, d) = f (b, d) in c
loop f b = fst $ let (c, d) = f (b, d) in (c, d)
loop f b = fst $ let x = f (b, d) in x
loop f b = fst $ let x = f' x in x
where f' (_, d) = f (b, d)
loop f b = fst $ fix $ f . (b,) . snd
反之亦然:
fix f = loop (join (,) . f . snd) ()
以上内容应该使您相信,loop
和fix
在谈论(->)
时具有同等的威力。那么,如果箭头意在泛化函数,为什么ArrowLoop
却没有这样定义?
class Arrow a => ArrowLoop a where
fix :: a b b -> b
箭头还概括了“过程”的概念:当Arrow a
时,a b c
是从c
计算b
的一种方式。如果将ArrowLoop
定义为直接概括fix
,那么它将严重受损。 fix
必须在没有任何上下文的情况下“执行”过程,并直接产生类型为b
的值,这意味着“过程” a b b
无法例如执行IO
。或者,考虑箭头
newtype LT i o = LT { runLT :: [i] -> [o] }
如果fix
会从[b]
中产生LT b b
,但您却不愿意,那么您会很喜欢。
loop
是解决这些限制的一种方法。它以过程为参数,并以结果为结果。从某种意义上说,与第一个过程相关的所有上下文都可以在第二个过程中保留下来,如果loop
更像fix
,那是不可能的。
请注意,我可以为fix
实现ArrowLoop
的类似物:
-- resulting process ignores its input
fix' :: ArrowLoop a -- taking an impl of loop as argument
=> a b b -> a u b
fix' f = loop ((id &&& id) . f . arr snd)
-- take off the outer application to () (application means (->), after all)
-- and arrowify: join (,) = id &&& id; snd = arr snd; (Prelude..) = (Control.Category..)
-- but the RHSs are more general
但我不相信
loop' :: Arrow a => (forall x u. a x x -> a u x) -- taking an impl of fix' as argument
-> a (b, d) (c, d) -> a b c
是可实现的,因此我们也不能以ArrowLoop
为基础的fix'
。