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

时间:2011-12-03 06:20:25

标签: haskell monads typeclass arrows

我在Monad.Reader#13读过Brent Yorgey撰写的“The Typeclassopedia”,发现“Functor hierachy”与“Category hierachy”相互依存,如图1所示。
<登记/> Figure.1

根据作者ArrowApply == Monad,特别是前一个只是一个可以在

时使用的类型类实例
  

“我们希望能够从中间结果计算箭头,并使用此计算箭头继续计算。这是ArrowApply给予我们的力量。”

但是我们怎样才能把这些东西放在一起呢?我的意思是Monad和Arrow都有一些流控制功能(如ifelseArrowChoice,或forMArrowLoop),并且Monad中的某些功能似乎“缺失”((***)(|||)first)。所有这些似乎都需要在使用Monad或Arrow系统来构建我们的副作用计算流程之间做出选择,并且会在另一个系统中丢失一些功能。

1 个答案:

答案 0 :(得分:12)

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

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

ArrowMonad newtype是我们为Monad箭头定义ArrowApply实例的工具。我们本来可以用

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

但是这会导致Haskell的有限类型推断出现问题(我会使用UndecideableInstances扩展,我想知道。

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

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)

因此我们知道ArrowApply 与<{1}}一样强大,因为我们可以在其中实现所有Monad操作。令人惊讶的是,反过来也是如此。这由Monad newtype给出,如@hammar所述。观察:

Kleisli

前面给出了使用monad操作的所有常规箭头操作的实现。未提及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) ,因为它在(***)first中有默认实现:

second

现在我们知道如何使用Monad操作实现箭头(f *** g = first f >>> second g ArrowArrowChoice)操作。


如果结果相同,请回答您关于为什么我们同时拥有ArrowApplyMonad的问题:

当我们不需要monad的全部功能时,功能较弱的箭头非常有用,就像应用函子可能很有用。即使ArrowArrowApply相同,但MonadArrow没有ArrowChoice的内容在app层次结构中无法表示。反之亦然,Monad在箭头层次结构中无法表示。 这是因为Applicative在monad层次结构中是“第一个”,在箭头层次结构中是“last”。

monad和箭头世界之间的主要语义差异是箭头捕获转换(ap表示我们从arr b c生成c),而monads捕获一个操作({ {1}}生成b)。这种差异很好地反映在monad aa新类型中:

Kleisli

ArrowMonad中,我们必须添加源类型newtype Kleisli m a b = Kleisli { runKleisli :: a -> m b } newtype ArrowApply a => ArrowMonad a b = ArrowMonad (a () b) ,并在Kleisli中将其设置为a


我希望这能满足你!