2015-10-15 64 views
6

为什么单声道不是monad是一个应用程序,而application是一个functor。你在网络上的许多文章中看到这个继承链(我经历过)。但是,当游戏者和应用者撰写Monad为什么打破这个?monad为什么不在scala中编写

有人可以在scala中提供一个简单的例子来演示这个问题吗?我知道这个问题很多,但没有一个简单的例子就很难理解。

+0

可能的重复[Applicatives撰写,单子不要](http://stackoverflow.com/questions/7040844/applicatives-compose-monads-dont) – ziggystar

+0

这是haskell答案不适用于scala – Jay

+4

检查出[答案由Conal](http://stackoverflow.com/a/7070339/108915)。它是语言不可知的。 Monad是一个数学概念,它们在语言之间没有区别。 – ziggystar

回答

9

首先,我们从一个简单的问题开始。比方说,我们需要得到两个整数的总和,这两个整数都包含在FutureOption中。我们假设,我们使用cats库。

如果我们用单子办法(又名flatMap),我们需要:

  • FutureOption应该对他们定义Monad实例
  • 我们还需要一元变压器OptionT将只能用于Option(精确地F[Option[T]]

所以,这里是代码(让我们忘记理解和提升使其更简单):

val fa = OptionT[Future, Int](Future(Some(1))) 
val fb = OptionT[Future, Int](Future(Some(2))) 
fa.flatMap(a => fb.map(b => a + b)) //note that a and b are already Int's not Future's 

如果你看看OptionT.flatMap来源:

def flatMap[B](f: A => OptionT[F, B])(implicit F: Monad[F]): OptionT[F, B] = 
    flatMapF(a => f(a).value) 

def flatMapF[B](f: A => F[Option[B]])(implicit F: Monad[F]): OptionT[F, B] = 
OptionT(F.flatMap(value)(_.fold(F.pure[Option[B]](None))(f))) 

你会发现,代码是相当具体到Option的内在逻辑和结构(foldNone)。同样的问题对于EitherTStateT

这里

重要的是,有没有猫定义FutureT,这样你就可以撰写Future[Option[T]],但不能做到这一点与Option[Future[T]](以后我会告诉这个问题甚至更通用)。

在另一方面,如果你选择使用Applicative组成,你必须满足只有一个要求:

  • FutureOption应该有Applicative情况下对他们

定义你不需要任何特殊的变形器Option,基本上猫图书馆提供Nested类适用于任何Applicative(让我们忘记适用建设者的糖来简化理解):

val fa = Nested[Future, Option, Int](Future(Some(1))) 
val fb = Nested[Future, Option, Int](Future(Some(1))) 
fa.map(x => (y: Int) => y + x).ap(fb) 

让我们交换他们:

val fa = Nested[Option, Future, Int](Some(Future(1))) 
val fb = Nested[Option, Future, Int](Some(Future(1))) 
fa.map(x => (y: Int) => y + x).ap(fb) 

作品!

所以,是的单子是应用型,Option[Future[T]]仍然是一个单子(上Future[T]但不能在T本身),但它可以让你只操作Future[T]T。为了“合并”OptionFuture层 - 您必须定义一元变换器FutureT,为了合并FutureOption - 您必须定义OptionT。并且,OptionT在cats/scalaz中定义,但不是FutureT

一般(从here):

不幸的是,我们真正的目标,单子组成,是相当多 困难。实际上,我们实际上可以证明,从某种意义上说, 没有办法构造一个上述类型的连接函数,只能使用两个monad的操作(参见附录中关于大纲 的证明)。由此可见,我们可能希望只有这样,才能形成 的组成是,如果有连接 双组份

一些额外的结构和该组合物中甚至没有可交换的,因为我已经显示了OptionFuture

作为练习,你可以尝试定义FutureT的flatMap:

def flatMapF[B](f: A => F[Future[B]])(implicit F: Monad[F]): FutureT[F, B] = 
    FutureT(F.flatMap(value){ x: Future[A] => 
     val r: Future[F[Future[B]] = x.map(f) 
     //you have to return F[Future[B]] here using only f and F.pure, 
     //where F can be List, Option whatever 
    }) 

基本符合这样的实现问题是,你必须从r这是不可能在这里‘提取’的价值,假设你可以不提取Future的值(没有定义它的comonad),如果我们正在讨论“非阻塞”的API,这是真的。这基本上意味着你不能“交换”FutureF,就像Future[F[Future[B]] => F[Future[Future[B]那样是自然变换(仿函数之间的态射)。这样解释了this general answer第一个注释:

您可以撰写单子,如果你能提供一个自然转化交换:纳米 - >明尼苏达州一个

Applicative小号但是没有这样的问题 - 你可以很容易地编写它们,但请记住,两个Applicatives的组合结果可能不是单子(但将永远是一个应用)。 Nested[Future, Option, T]不是T上的monad,无论是Option还是Future都是T上的monad。用简单的词汇Nested as a class没有flatMap

这将是也有利于阅读:

全部放在一起(FG是单子)

  • F[G[T]]G[T]一个单子,但不是在T
  • G_TRANSFORMER[F, T]为了从F[G[T]]得到T单子需要。
  • 没有MEGA_TRANSFORMER[G, F, T]因此变压器不能建立在单子上面 - 它需要G定义的附加操作(好像在G comonad应该足够)
  • 每一个单子(包括GF)被但不是每一个应用都是单粒子
  • 理论上F[G[T]]适用于G[T]T。然而,斯卡拉需要创建NESTED[F, G, T]为了得到T(它是在猫库中实现)组成应用。
  • NESTED[F, G, T]是合用的,但没有一个单子

这意味着你可以撰写Future x Option(又名Option[Future[T]])到一个单一的单子,但你不能在不知道撰写Option x Future(又名Future[Option[T]]),他们是什么除了单子之外。你可以将任何两个应用程序组合成一个应用程序,你也可以将任意两个monad组合成一个单一的应用程序(但不是一个单一的monad)。

0

托尼莫里斯就单子变压器进行了一次演讲,这很好地解释了这个准确的问题。

http://tonymorris.github.io/blog/posts/monad-transformers/

他用哈斯克尔,但实例容易翻译到阶。

+0

我想避免这个haskell语法,因为它更难以阅读,特别是对于这个主题的初学者!所以我更喜欢简单的scala示例,这是可以理解的。 Thx – Jay

+1

也许你应该为Functors编写一个组合函数,然后尝试为Monads写一个函数 - 它会给你一个很好的见解。 – melps

+2

这是一个很好的资源,对相关部分的总结会是一个很好的答案,但是对于自己的外部资源的链接应该是评论,而不是答案。 –

相关问题