修复与ArrowLoop

时间:2018-08-09 03:27:20

标签: loops haskell fixed-point arrows

loopControl.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

所以我的问题是:

  1. 是否可以通过loop来实现特定的fix
  2. 它们的功能有何不同?

1 个答案:

答案 0 :(得分:8)

  1. 当然可以。每个递归定义都可以用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) ()
    
  2. 以上内容应该使您相信,loopfix在谈论(->)时具有同等的威力。那么,如果箭头意在泛化函数,为什么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'