【问题标题】:Arrow and Monad, two independent viewpoints to compose computations?Arrow 和 Monad,两个独立的观点来组成计算?
【发布时间】:2012-01-12 01:16:07
【问题描述】:

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

@ 987654324@

而且按照作者ArrowApply == Monad的说法,特别是前面那个只是一个类型类实例,可以当

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

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

【问题讨论】:

  • 我不确定您的问题到底是什么,但您可以在 KleisliArrowMonad 新类型中看到单子和箭头之间的一些联系。
  • 我的问题是:为什么 Arrow* 有这么多我可以在 Monad 中找到的类似东西?如果 ArrowApply 是 Monad,为什么 Haskell 需要两个并行的类型类层次结构来做同样的事情(或者不是?)?

标签: haskell monads typeclass arrows


【解决方案1】:

答案就在下面(全部来自Control.Arrow docs

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

ArrowMonad newtype 是我们为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 给出的。观察:

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 操作来实现箭头(ArrowArrowChoiceArrowApply)操作。


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

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

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

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

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


希望你满意!

【讨论】:

  • 谢谢!我会花一些时间研究您稍后在答案中提到的那些类型(我所在地区是 23:00 :))。
  • 不错!当我们继续想象 Haskell Prime 基础库应该是什么样子时,肯定需要考虑一些事情。
  • monad 层次结构中的apArrowApplyapp 的作用不同,就像在前面操作生成的运行代码中一样。在 monad 层次结构和 Arrow 层次结构中,这都出现在 Monad==ArrowApply 点,在 monads 中有 >>=,它允许 produceMonadicValue >>= idapp 在允许 produceArrowValue >>> app 的箭头中。 Arrow 已经可以做 Applicative 的 ap(<*>) 的等价物:如果我们有箭头 Amakef :: A () (x->y)makex :: A () x 我们可以做 makef &&& makex >>> pure (uncurry ($))makef' <*> makex' 一样。跨度>
  • instance ArrowApply a => Monad (a ()) 不需要UndecidableInstances,只需要FlexibleInstances
猜你喜欢
  • 2013-01-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-16
  • 2014-06-17
  • 1970-01-01
  • 2013-09-26
相关资源
最近更新 更多