17

我在Monad.Reader#13中阅读了 Brent Yorgey 的“The Typeclassopedia” ,发现“Functor hierachy”与“the Category hierachy”相互依赖,如图 1 所示。

图1

而且根据作者的说法ArrowApply == Monad,特别是前面那个只是一个类型类实例,可以在什么时候使用

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

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

4

1 回答 1

12

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

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

newtypeArrowMonad是我们用来定义箭头Monad实例的车辆。ArrowApply我们本可以使用

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

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

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

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强大Monad因为我们可以Monad在其中实现所有操作。令人惊讶的是,反过来也是如此。这是由Kleisli@hammar 指出的新类型给出的。观察:

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

所以现在我们知道了如何使用 Monad 操作来实现箭头 ( Arrow, ArrowChoice, ArrowApply) 操作。


要回答您关于为什么我们同时拥有两者Monad以及Arrow它们是否相等的问题:

当我们不需要 monad 的全部功能时,功能较弱的箭头很有用,就像应用函子很有用一样。即使ArrowApplyandMonad是等价的,an Arrowor ArrowChoicewithout是在层次结构app中不可表示的东西。Monad反之亦然,anApplicative在箭头层次结构中不可表示。这是因为ap在单子层次结构中出现“第一”,在箭头层次结构中出现“最后”。

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

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

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


我希望这能让你满意!

于 2011-12-03T10:54:34.127 回答