为什么Monad无法在Scala中编写 [英] Why do monads not compose in scala
问题描述
当Monad是Applicative且Applicative是Functor时,为什么Monad不组成.您可以在网络上的许多文章(我已经读过)中看到这个继承链.但是当Functors和Applicatives组成时,为什么Monads打破了这个?
Why do monads not compose when a Monad is an Applicative and an Applicative is a Functor. You see this inheritance chain in many articles on the web ( Which i have gone through ). But when Functors and Applicatives compose why do Monads break this ?
有人可以在scala中提供一个简单的示例来演示此问题吗?我知道这个问题很多,但没有一个简单的例子就很难理解.
Can someone provide a simple example in scala which demonstrates this issue ? I know this is asked a lot but kind of hard to understand without a simple example.
推荐答案
首先,让我们从一个简单的问题开始.假设我们需要得到两个整数的和,每个整数都包裹在Future
和Option
中.让我们使用cats
库,以使Haskell的标准库定义与Scala语法相似.
First, let's start with a simple problem. Let's say, we need to get a sum of two integers, each wrapped in both Future
and Option
. Let's take cats
library in order to resemble Haskell’s standard library definitions with Scala-syntax.
如果我们使用monad方法(又名flatMap
),则需要:
If we use monad approach (aka flatMap
), we need:
-
Future
和Option
都应在其上定义Monad
个实例 - 我们还需要一元变压器
OptionT
,该变压器仅适用于Option
(确切地说是F[Option[T]]
)
- both
Future
andOption
should haveMonad
instances defined over them - we also need monadic transformer
OptionT
which will work only forOption
(preciselyF[Option[T]]
)
因此,这是代码(让我们省去了理解和提升,使其变得更简单):
So, here is the code (let's forget about for-comprehension and lifting to make it simpler):
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
等的相同问题.
You'll notice that the code is pretty specific to Option
's internal logic and structure (fold
, None
). Same problem for EitherT
, StateT
etc.
这里重要的是,在cats中没有定义FutureT
,因此您可以编写Future[Option[T]]
,但不能使用Option[Future[T]]
进行编写(稍后,我将证明此问题更为普遍)
Important thing here is that there is no FutureT
defined in cats, so you can compose Future[Option[T]]
, but can't do that with Option[Future[T]]
(later I'll show that this problem is even more generic).
另一方面,如果您使用Applicative
选择合成,则只需要满足一个要求:
On the other hand, if you choose composition using Applicative
, you'll have to meet only one requirement:
-
Future
和Option
都应在其上定义Applicative
个实例
- both
Future
andOption
should haveApplicative
instances defined over them
Option
不需要任何特殊的转换器,基本上cats库提供了适用于任何Applicative
的Nested
类(为简化理解,请忽略应用构建器的糖):
You don't need any special transformers for Option
, basically cats library provides Nested
class that works for any Applicative
(let's forget about applicative builder's sugar to simplify understanding):
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)
让我们掉期期权和期货:
Let's swap Option and Future:
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)
有效!
是的,所以Monad是适用的,Option[Future[T]]
仍然是monad(在Future[T]
上,但不在T
上),但是它仅允许您使用Future[T]
而不是T
进行操作.为了将Option
与Future
层合并"-您必须定义单变变压器FutureT
,为了将Future
与Option
合并-您必须定义OptionT
.而且,OptionT
是在cats/scalaz中定义的,但不是FutureT
.
So yes Monad is Applicative, Option[Future[T]]
is still a monad (on Future[T]
but not on T
itself) but it allows you to operate only with Future[T]
not T
. In order to "merge" Option
with Future
layers - you have to define monadic transformer FutureT
, in order to merge Future
with Option
- you have to define OptionT
. And, OptionT
is defined in cats/scalaz, but not FutureT
.
通常(来自此处):
不幸的是,我们真正的目标,即单子的组成,还更多 难的. ..实际上,我们可以从某种意义上证明 没有办法使用上面的类型构造一个join函数 仅两个monad的操作(有关概述,请参阅附录 证明).因此,我们可能希望形成的唯一方式 构图是是否还有其他构造链接 这两个组成部分
Unfortunately, our real goal, composition of monads, is rather more difficult. .. In fact, we can actually prove that, in a certain sense, there is no way to construct a join function with the type above using only the operations of the two monads (see the appendix for an outline of the proof). It follows that the only way that we might hope to form a composition is if there are some additional constructions linking the two component
而且,正如我为Option
和Future
演示的那样,此组合甚至不是必需的可交换(可交换).
And this composition is not even necessary commutative (swappable) as I demonstrated for Option
and Future
.
作为练习,您可以尝试定义FutureT
的flatMap:
As an exercise, you can try to define FutureT
's 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
})
基本上,这种实现的问题是,您必须至少从"c"中提取"r"的值,这在这里是不可能的,前提是您至少不能从"Future
"中提取值(没有定义comonad).非阻塞"上下文(例如ScalaJs).这基本上意味着您不能像Future[F[Future[B]] => F[Future[Future[B]
一样交换" Future
和F
.后者是自然变换(函子之间的同构),因此可以解释对此一般答案的第一个评论:
basically the problem with such implementation is that you have to "extract" value from r which is impossible here, assuming you can't extract value from Future
(there is no comonad defined on it) at least in a "non-blocking" context (like ScalaJs). This basically means that you can't "swap" Future
and F
, like Future[F[Future[B]] => F[Future[Future[B]
. The latter is a natural transformation (morphism between functors), so that explains the first comment on this general answer:
如果您可以提供自然转换交换,则可以编写monad:N M a-> M N a
you can compose monads if you can provide a natural transformation swap : N M a -> M N a
Applicative
却没有这样的问题-您可以轻松地将它们组合在一起,但是请记住,由两个Applicatives
组成的结果可能不是monad(但始终是可应用的). Nested[Future, Option, T]
不是T
上的单子,无论Option
和Future
都是T
上的单子.用简单的文字嵌套为类没有flatMap
.
Applicative
s however don't have such problems - you can easily compose them, but keep in mind that result of composition of two Applicatives
may not be a monad (but will always be an applicative). Nested[Future, Option, T]
is not a monad on T
, regardless that both Option
and Future
are monads on T
. Putting in simple words Nested as a class doesn't have flatMap
.
阅读以下内容也会有帮助:
It would be also helpful to read:
- http://typelevel.org/cats/tut/applicative.html
- http://typelevel.org/cats/tut/apply.html
- http://typelevel.org/cats/tut/monad.html
- http://typelevel.org/cats/tut/optiont.html
- http://typelevel.org/cats/tut/applicative.html
- http://typelevel.org/cats/tut/apply.html
- http://typelevel.org/cats/tut/monad.html
- http://typelevel.org/cats/tut/optiont.html
将它们全部放在一起(F
和G
是单子)
Putting it all together (F
and G
are monads)
-
F[G[T]]
是G[T]
上的单子,但不是T
上的单子.
要从 -
G_TRANSFORMER[F, T]
. - 没有
MEGA_TRANSFORMER[G, F, T]
,因为这样的转换器不能在monad之上构建-它需要在G
上定义的其他操作(看来G
上的comonad应该足够了) - 每个monad(包括
G
和F
)都是可应用的,但并非每个可应用的对象都是一个monad - 理论上,
F[G[T]]
是G[T]
和T
的适用语.但是,scala需要创建NESTED[F, G, T]
才能在T
上组合使用(在cats库中实现). -
NESTED[F, G, T]
适用,但不是monad
F[G[T]]
获得T
上的monad,需要F[G[T]]
is a monad onG[T]
, but not onT
G_TRANSFORMER[F, T]
required in order to get a monad onT
fromF[G[T]]
.- there is no
MEGA_TRANSFORMER[G, F, T]
as such transformer can't be build on top of monad - it requires additional operations defined onG
(it seems like comonad onG
should be enough) - every monad (including
G
andF
) is applicative, but not every applicative is a monad - in theory
F[G[T]]
is an applicative over bothG[T]
andT
. However scala requires to createNESTED[F, G, T]
in order to get composed applicative onT
(which is implemented in cats library). NESTED[F, G, T]
is applicative, but not a monad
这意味着您可以将Future x Option
(aka Option[Future[T]]
)合成为一个单子(因为存在coz OptionT
),但是如果您不知道Future是某种东西,就不能组成Option x Future
(aka Future[Option[T]]
).除了成为monad以外(即使它们本质上是可应用的函子-不能仅在其上构建monad或monad变换器也不是可应用的).基本上:
That means you can compose Future x Option
(aka Option[Future[T]]
) to one single monad (coz OptionT
exists), but you can't compose Option x Future
(aka Future[Option[T]]
) without knowing that Future is something else besides being a monad (even though they’re inherently applicative functors - applicative is not enough to neither build a monad nor monad transformer on it) . Basically:
-
OptionT
可以看作是定义为OptionT: Monad[Option] x Monad[F] -> OptionT[F, T]; for all Monad[F], T; for some F[T]
的非可交换二进制运算符.或一般来说:Merge: Monad[G] x Monad[F] -> Monad[Merge]; for all T, Monad[F]; but only for **some of Monad[G]**, some F[T], G[T]
;
OptionT
can be seen as non-commutative binary operator defined asOptionT: Monad[Option] x Monad[F] -> OptionT[F, T]; for all Monad[F], T; for some F[T]
. Or in general:Merge: Monad[G] x Monad[F] -> Monad[Merge]; for all T, Monad[F]; but only for **some of Monad[G]**, some F[T], G[T]
;
您可以将任意两个应用程序组成一个应用程序Nested: Applicative[F] x Applicative[G] -> Nested[F, G]; for all Applicative[F], Applicative[G], T; for some F[T], G[T]
you can compose any two applicatives into one single applicative Nested: Applicative[F] x Applicative[G] -> Nested[F, G]; for all Applicative[F], Applicative[G], T; for some F[T], G[T]
,
,但是您只能将任意两个monad(固有的仿函数)组合为一个 applicative (但不能组合为monad).
but you can compose any two monads (inherently functors) only into one applicative (but not into monad).
这篇关于为什么Monad无法在Scala中编写的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!