【问题标题】:Is it possible to implement MonadFix for `Free`?是否可以为“免费”实现 MonadFix?
【发布时间】:2013-01-16 03:50:53
【问题描述】:

http://hackage.haskell.org/package/free in Control.Monad.Free.Free 允许访问任何给定Functor 的“免费单子”。但是,它没有MonadFix 实例。这是因为这样的实例无法编写,还是被遗漏了?

如果这样的实例不能写,为什么不呢?

【问题讨论】:

    标签: haskell monadfix free-monad


    【解决方案1】:

    考虑mfix 所做的描述:

    一元计算的不动点。 mfix f 只执行一次操作 f,最终输出作为输入反馈。

    Free 的上下文中,“执行”一词表示创建Functor 的层。因此,“仅一次”意味着在评估 mfix f 的结果中,Pure 构造函数中保存的值必须完全确定创建了多少层仿函数。

    现在,假设我们有一个特定的函数once,我们知道它总是只会创建一个Free 构造函数,但是需要许多Pure 构造函数来保存叶值。那么,'once' 的输出将只有Free f a 类型的值与f a 类型的某个值同构。有了这些知识,我们就可以安全地 un-Free once 的输出,得到 f a 类型的值。

    现在,请注意,因为mfix 需要“只执行一次操作”,所以对于符合要求的实例,mfix once 的结果应该不包含比once 在单个应用程序中创建的额外的一元结构。因此我们可以推断,从mfix once 获得的值也必须与f a 类型的值同构。

    给定任何a -> f a类型的函数对于一些Functorf,我们可以用单个Free包装结果并得到满足上面once描述的a -> Free f a类型的函数,并且我们已经确定我们可以解开 mfix once 的结果以返回 f a 类型的值。

    因此,符合条件的实例 (Functor f) => MonadFix (Free f) 意味着能够通过上述包装和展开来编写函数 ffix :: (Functor f) => (a -> f a) -> f a,该函数适用于 Functor 的所有实例。

    这显然不能证明您不能编写这样的实例...但是如果可能的话,MonadFix 将是完全多余的,因为您可以轻松地编写 @987654353 @ 直接地。 (我想用Monad 约束将它重新实现为mfix,使用liftM。呃。)

    【讨论】:

    • 到目前为止,我发现如果我可以为给定的f 实现retractFunctor :: f a -> a,那么我可以实现mfix f = fix (f . iter retractFunctor)。我不确定这是否是正确的方法。
    • @singpolyma:我最初的想法是至少应该可以编写实例(Functor m, MonadFix m) => MonadFix (Free m)。但我不知道如何正确地做到这一点。
    • 这个结果也很有意义,因为“免费”意味着它应该给你一个单子仅此而已。但是MonadFix 不是Monad 的一部分,事实上,如果Monad 暗示了它,那么它就是多余的。你能通过向Free 添加额外的结构来创建一个“免费的MonadFix”吗? (在为Fix (a -> Free f a) 构造函数编写fmap 时,我的天真尝试被搁置了......)
    • @illissius:是的,如果类型参数处于逆变位置,您可能不会走得太远......我必须考虑一下,我更关心的是找出什么额外的结构在f 上将允许mfix,而不是使用Free 以外的东西。
    • @illissius,您可以通过将构造函数 forall s. Fix (s -> Free f (s,a)) 添加到 Free 来制作免费的 monadfix。它不会严格满足法律要求;然而,这可以通过要求某些东西成为有效消费者的属性来修补(“它不能根据看到Fix 构造函数做出任何重要决定”的一些正式变体;例如其中一个可能是f (Fix (\s -> Pure (s,x)) = f (Pure x))。
    【解决方案2】:

    好吧,受MaybeMonadFix 实例的启发,我尝试了这个(使用Free 的以下定义):

    data Free f a
        = Pure a
        | Impure (f (Free f a))
    
    instance (Functor f) => Monad (Free f) where
        return = Pure
        Pure x >>= f = f x
        Impure x >>= f = Impure (fmap (>>= f) x)
    
    instance (Functor f) => MonadFix (Free f) where
        mfix f = let Pure x = f x in Pure x
    

    The laws 是:

    • 纯度:mfix (return . h) = return (fix h)
    • 左缩:mfix (\x -> a >>= \y -> f x y) = a >>= \y -> mfix (\x -> f x y)
    • 滑动:mfix (liftM h . f) = liftM h (mfix (f . h)),对于严格的h
    • 嵌套:mfix (\x -> mfix (f x)) = mfix (\x -> f x x)

    纯度很容易证明——但我在尝试证明左缩时遇到了一个问题:

    mfix (\x -> a >>= \y -> f x y)
    = let Pure x = (\x -> a >>= \y -> f x y) x in Pure x
    = let Pure x = a >>= \y -> f x y in Pure x
    -- case a = Pure z
      = let Pure x = Pure z >>= \y -> f x y in Pure x
      = let Pure x = f x z in Pure x
      = let Pure x = (\x -> f x z) x in Pure x
      = mfix (\x -> f x z)
      = Pure z >>= \y -> mfix (\x -> f x y)
    -- case a = Impure t
      = let Pure x = Impure t >>= \y -> f x y in Pure x
      = let Pure x = Impure (fmap (>>= \y -> f x y) t) in Pure x
      = Pure _|_
    

    但是

      Impure t >>= \y -> mfix (\x -> f x y)
      = Impure (fmap (>>= \y -> mfix (\x -> f x y)) t)
      /= Pure _|_
    

    因此,至少,如果 PureImpure 构造函数是可区分的,那么我对 mfix 的实现不符合法律。我想不出任何其他的实现,但这并不意味着它不存在。抱歉,我无法进一步说明。

    【讨论】:

    • 我在想——如果区分PureImpure 不是你应该能够做的事情;即它感觉就像,明确地说,Free Identity 应该与Identity 同构。 Category-privy,我在基地吗?
    【解决方案3】:

    不,不能笼统地写出来,因为不是每个 Monad 都是 MonadFix 的实例。 每个 Monad 都可以使用下面的 FreeMonad 来实现。如果您可以免费实现 MonadFix,那么您将能够从任何 Monad 派生 MonadFix,这是不可能的。 但当然,您可以为 MonadFix 类定义一个 FreeFix。

    我认为它可能看起来像这样,但这只是第三次猜测(尚未测试):

    data FreeFix m a = FreeFix { runFreeFix :: (forall r. (r -> m r) -> m r) -> m a }
    
    instance (Monad m) => Monad (FreeFix m) where
        return a = FreeFix $ \_-> do
            return a
        f >>= g = FreeFix $ \mfx -> do
            x <- runFreeFix f mfx
            runFreeFix (g x) mfx
    
    instance (Monad m) => MonadFix (FreeFix m) where
        mfix f = FreeFix $ \mfx -> do
            mfx (\r->runFreeFix (f r) mfx)
    

    这个想法是 m 是一个缺少 mfix 实现的 Monad;所以当 FreeFix 会被缩减时,mfix 需要作为一个参数。

    【讨论】:

    • FreeFix m 不是函子。但是您可以使用 Arrow 的 loop 组合器技术使其成为一个:| forall r. Fix (r -&gt; FreeFix m (r,a))
    • @luqui:不知怎的,我从你的建议中得到了这个递归方案,不知道如何。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-04-21
    • 2020-02-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-31
    • 2017-07-14
    相关资源
    最近更新 更多