为什么单声道不是monad是一个应用程序,而application是一个functor。你在网络上的许多文章中看到这个继承链(我经历过)。但是,当游戏者和应用者撰写Monad为什么打破这个?monad为什么不在scala中编写
有人可以在scala中提供一个简单的例子来演示这个问题吗?我知道这个问题很多,但没有一个简单的例子就很难理解。
为什么单声道不是monad是一个应用程序,而application是一个functor。你在网络上的许多文章中看到这个继承链(我经历过)。但是,当游戏者和应用者撰写Monad为什么打破这个?monad为什么不在scala中编写
有人可以在scala中提供一个简单的例子来演示这个问题吗?我知道这个问题很多,但没有一个简单的例子就很难理解。
首先,我们从一个简单的问题开始。比方说,我们需要得到两个整数的总和,这两个整数都包含在Future
和Option
中。我们假设,我们使用cats
库。
如果我们用单子办法(又名flatMap
),我们需要:
Future
和Option
应该对他们定义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
的内在逻辑和结构(fold
,None
)。同样的问题对于EitherT
,StateT
等
重要的是,有没有猫定义FutureT
,这样你就可以撰写Future[Option[T]]
,但不能做到这一点与Option[Future[T]]
(以后我会告诉这个问题甚至更通用)。
在另一方面,如果你选择使用Applicative
组成,你必须满足只有一个要求:
Future
和Option
应该有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
。为了“合并”Option
和Future
层 - 您必须定义一元变换器FutureT
,为了合并Future
和Option
- 您必须定义OptionT
。并且,OptionT
在cats/scalaz中定义,但不是FutureT
。
一般(从here):
不幸的是,我们真正的目标,单子组成,是相当多 困难。实际上,我们实际上可以证明,从某种意义上说, 没有办法构造一个上述类型的连接函数,只能使用两个monad的操作(参见附录中关于大纲 的证明)。由此可见,我们可能希望只有这样,才能形成 的组成是,如果有连接 双组份
一些额外的结构和该组合物中甚至没有可交换的,因为我已经显示了Option
和Future
。
作为练习,你可以尝试定义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,这是真的。这基本上意味着你不能“交换”Future
和F
,就像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
。
这将是也有利于阅读:
全部放在一起(F
和G
是单子)
F[G[T]]
是G[T]
一个单子,但不是在T
G_TRANSFORMER[F, T]
为了从F[G[T]]
得到T
单子需要。MEGA_TRANSFORMER[G, F, T]
因此变压器不能建立在单子上面 - 它需要G
定义的附加操作(好像在G
comonad应该足够)G
和F
)被但不是每一个应用都是单粒子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)。
托尼莫里斯就单子变压器进行了一次演讲,这很好地解释了这个准确的问题。
http://tonymorris.github.io/blog/posts/monad-transformers/
他用哈斯克尔,但实例容易翻译到阶。
可能的重复[Applicatives撰写,单子不要](http://stackoverflow.com/questions/7040844/applicatives-compose-monads-dont) – ziggystar
这是haskell答案不适用于scala – Jay
检查出[答案由Conal](http://stackoverflow.com/a/7070339/108915)。它是语言不可知的。 Monad是一个数学概念,它们在语言之间没有区别。 – ziggystar