我在Monad.Reader#13读过Brent Yorgey撰写的“The Typeclassopedia”,发现“Functor hierachy”与“Category hierachy”相互依存,如图1所示。
<登记/>
根据作者ArrowApply == Monad
,特别是前一个只是一个可以在
“我们希望能够从中间结果计算箭头,并使用此计算箭头继续计算。这是ArrowApply给予我们的力量。”
但是我们怎样才能把这些东西放在一起呢?我的意思是Monad和Arrow都有一些流控制功能(如if
和else
与ArrowChoice
,或forM
与ArrowLoop
),并且Monad中的某些功能似乎“缺失”((***)
,(|||)
或first
)。所有这些似乎都需要在使用Monad或Arrow系统来构建我们的副作用计算流程之间做出选择,并且会在另一个系统中丢失一些功能。
答案 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
,Arrow
,ArrowChoice
)操作。
如果结果相同,请回答您关于为什么我们同时拥有ArrowApply
和Monad
的问题:
当我们不需要monad的全部功能时,功能较弱的箭头非常有用,就像应用函子可能很有用。即使Arrow
和ArrowApply
相同,但Monad
或Arrow
没有ArrowChoice
的内容在app
层次结构中无法表示。反之亦然,Monad
在箭头层次结构中无法表示。
这是因为Applicative
在monad层次结构中是“第一个”,在箭头层次结构中是“last”。
monad和箭头世界之间的主要语义差异是箭头捕获转换(ap
表示我们从arr b c
生成c
),而monads捕获一个操作({ {1}}生成b
)。这种差异很好地反映在monad a
和a
新类型中:
Kleisli
在ArrowMonad
中,我们必须添加源类型newtype Kleisli m a b = Kleisli { runKleisli :: a -> m b }
newtype ArrowApply a => ArrowMonad a b = ArrowMonad (a () b)
,并在Kleisli
中将其设置为a
。
我希望这能满足你!