Arrow和Monad,两个独立的视点来组成计算?

发布于 2024-12-19 05:36:54 字数 777 浏览 7 评论 0原文

我读过 Brent Yorgey 在 Monad.Reader 中写的“The Typeclassopedia” #13,发现“Functor层次结构”与“Category层次结构”相互依赖,如图1所示。

图.1

而且根据作者的说法,ArrowApply == Monad,尤其是前一个只是一个类型类实例,可以在以下情况下使用:

“我们希望能够根据中间结果计算出一个箭头,并使用这个计算出的箭头继续计算。这是 ArrowApply 赋予我们的能力。”

但我们怎样才能把这些东西放在一起呢?我的意思是 Monad 和 Arrow 中都有一些流程控制函数(例如 ifelseArrowChoiceforM< /code> 与 ArrowLoop),并且 Monad 中的某些功能似乎“缺失”((***),(|||)首先)。所有这些看起来就像我们需要在使用 Monad 或 Arrow 系统来构建我们的副作用计算流程之间做出选择,并且会失去另一个系统中的一些功能。

I've reading the "The Typeclassopedia" by Brent Yorgey in Monad.Reader#13 ,and found that "the Functor hierachy" is interdependent of "the Category hierachy" as the Figure.1 shown.

Figure.1

And according to the author, ArrowApply == Monad, especially that the previous one is just a type class instance that can be used when

"we would like to be able to compute an arrow from intermediate results, and use this computed arrow to continue the computation. This is the power given to us by ArrowApply."

But how can we put these things together ? I mean that there are some flow control functions both in Monad and Arrow ( like if and else vs. ArrowChoice, or forM vs. ArrowLoop), and some features seem like "missing" in Monad ( (***),(|||) or first). All these are seem like that we need to make a choice between using Monad or Arrow system to construct our side effect computation flow, and will lose some features in another system.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

痴情 2024-12-26 05:36:54

答案如下(所有这些都来自 Control.Arrow 文档

newtype ArrowApply a => ArrowMonad a b = ArrowMonad (a () b)
instance Monad ArrowApply a => Monad (ArrowMonad a)

ArrowMonad 新类型是我们为 ArrowApply 箭头定义 Monad 实例的载体。我们本来可以使用

instance Monad ArrowApply a => Monad (a ())

,但这会导致 Haskell 的有限类型类推断出现问题(我认为它可以与 UndecideableInstances 扩展一起使用)。

您可以将 ArrowApply 箭头的 Monad 实例视为将单子操作转换为等效的箭头操作,如源代码所示:

instance ArrowApply a => Monad (ArrowMonad a) where
        return x = ArrowMonad (arr (\_ -> x))
        ArrowMonad m >>= f = ArrowMonad (m >>>
                        arr (\x -> let ArrowMonad h = f x in (h, ())) >>>
                        app)

所以知道我们知道ArrowApplyMonad 一样强大,因为我们可以在其中实现所有 Monad 操作。令人惊讶的是,反之亦然。这是由 @hammar 指出的 Kleisli 新类型给出的。观察:

newtype Kleisli m a b = Kleisli { runKleisli :: a -> m b }

instance Monad m => Category (Kleisli m) where
        id = Kleisli return
        (Kleisli f) . (Kleisli g) = Kleisli (\b -> g b >>= f)

instance Monad m => Arrow (Kleisli m) where
        arr f = Kleisli (return . f)
        first (Kleisli f) = Kleisli (\ ~(b,d) -> f b >>= \c -> return (c,d))
        second (Kleisli f) = Kleisli (\ ~(d,b) -> f b >>= \c -> return (d,c))

instance Monad m => ArrowApply (Kleisli m) where
        app = Kleisli (\(Kleisli f, x) -> f x)

instance Monad m => ArrowChoice (Kleisli m) where
    left f = f +++ arr id
    right f = arr id +++ f
    f +++ g = (f >>> arr Left) ||| (g >>> arr Right)
    Kleisli f ||| Kleisli g = Kleisli (either f g)

前面给出了使用 monad 操作的所有常见箭头操作的实现。 (***) 没有被提及,因为它有一个使用 firstsecond 的默认实现:

f *** g = first f >>> second g

所以现在我们知道如何实现箭头 (< code>Arrow、ArrowChoiceArrowApply)使用 Monad 操作进行操作。


回答你关于为什么我们同时拥有 MonadArrow 的问题(如果它们是等价的):

当我们不需要箭头的全部功能时,功能较弱的箭头很有用。 monad,就像应用函子一样有用。尽管 ArrowApplyMonad 是等效的,但没有 appArrowArrowChoice是在 Monad 层次结构中无法表示的东西。反之亦然,Applicative 无法在箭头层次结构中表示。
这是因为 ap 在 monad 层次结构中是“第一个”,在箭头层次结构中是“最后一个”。

单子世界和箭头世界之间的主要语义区别在于箭头捕获转换(arr b c 意味着我们从 b 生成 c),而 monad 捕获一个操作(monad a 生成一个 a)。这种差异在 KleisliArrowMonad 新类型中得到了很好的体现:

newtype Kleisli m a b = Kleisli { runKleisli :: a -> m b }
newtype ArrowApply a => ArrowMonad a b = ArrowMonad (a () b)

Kleisli 中,我们必须添加源类型 a ,并在 ArrowMonad 中我们将其设置为 ()


我希望这能让您满意!

The answer lies in the following (all of this is from the Control.Arrow docs)

newtype ArrowApply a => ArrowMonad a b = ArrowMonad (a () b)
instance Monad ArrowApply a => Monad (ArrowMonad a)

The ArrowMonad newtype is the vehicle with which we define the Monad instance for ArrowApply arrows. We could have used

instance Monad ArrowApply a => Monad (a ())

but this would've caused problems with Haskell's limited type class inference (It would work with the UndecideableInstances extension, I fathom).

You can think of the Monad instance for ArrowApply arrows as translating monadic operations into equivalent arrow operations, as the source shows:

instance ArrowApply a => Monad (ArrowMonad a) where
        return x = ArrowMonad (arr (\_ -> x))
        ArrowMonad m >>= f = ArrowMonad (m >>>
                        arr (\x -> let ArrowMonad h = f x in (h, ())) >>>
                        app)

So know we know that ArrowApply is as powerful as Monad since we can implement all of the Monad operations in it. Surprisingly, the converse is also true. This is given by the Kleisli newtype as @hammar noted. Observe:

newtype Kleisli m a b = Kleisli { runKleisli :: a -> m b }

instance Monad m => Category (Kleisli m) where
        id = Kleisli return
        (Kleisli f) . (Kleisli g) = Kleisli (\b -> g b >>= f)

instance Monad m => Arrow (Kleisli m) where
        arr f = Kleisli (return . f)
        first (Kleisli f) = Kleisli (\ ~(b,d) -> f b >>= \c -> return (c,d))
        second (Kleisli f) = Kleisli (\ ~(d,b) -> f b >>= \c -> return (d,c))

instance Monad m => ArrowApply (Kleisli m) where
        app = Kleisli (\(Kleisli f, x) -> f x)

instance Monad m => ArrowChoice (Kleisli m) where
    left f = f +++ arr id
    right f = arr id +++ f
    f +++ g = (f >>> arr Left) ||| (g >>> arr Right)
    Kleisli f ||| Kleisli g = Kleisli (either f g)

The previous gives implementations for all of the usual arrow operations using monad operations. (***) is not mentioned since it has a default implementation usin first and second:

f *** g = first f >>> second g

So now we know how to implement arrow (Arrow, ArrowChoice, ArrowApply) operations using Monad operations.


To answer your question about why we have both Monad and Arrow if they turn out to be equivalent:

The less powerful arrows are useful when we do not need the full power of a monad, just like applicative functors can be useful. And even though ArrowApply and Monad are equivalent, an Arrow or ArrowChoice without app is something that is not representable in the Monad hierarchy. Vice versa, an Applicative is not representable in the arrow hierarchy.
This is because ap comes "first" in the monad hierarchy and "last" in the arrow hierarchy.

The main semantic difference between the monad and arrow worlds is that arrows capture a transformation (arr b c means we produce a c from a b), while monads capture an operation (monad a produces an a). This difference is reflected well in the Kleisli and ArrowMonad newtypes:

newtype Kleisli m a b = Kleisli { runKleisli :: a -> m b }
newtype ArrowApply a => ArrowMonad a b = ArrowMonad (a () b)

In Kleisli we have to add the source type a, and in ArrowMonad we set it to ().


I hope this satisfies you!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文