为什么monads不在scala中组合
Why do monads not compose in scala
当 Monad 是 Applicative 而 Applicative 是 Functor 时,为什么 monad 不组合。你在网络上的许多文章中看到了这个继承链(我已经经历过)。但是,当 Functor 和 Applicatives 组合时,为什么 Monad 会破坏它?
有人可以提供一个简单的 scala 示例来说明这个问题吗?我知道这被问了很多,但如果没有一个简单的例子就很难理解。
托尼·莫里斯 (Tony Morris) 发表了关于 monad 转换器的演讲,很好地解释了这个确切的问题。
http://tonymorris.github.io/blog/posts/monad-transformers/
他使用 haskell,但示例很容易翻译成 scala。
首先,让我们从一个简单的问题开始。比方说,我们需要得到两个整数的总和,每个整数都包含在 Future
和 Option
中。让我们使用 cats
库来类似于 Haskell 的标准库定义和 Scala 语法。
如果我们使用 monad 方法(又名 flatMap
),我们需要:
Future
和 Option
都应该在它们之上定义 Monad
个实例
- 我们还需要 monadic 转换器
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
等
同样的问题
这里重要的是在cats中没有定义FutureT
,所以你可以写Future[Option[T]]
,但不能用Option[Future[T]]
来写(稍后我会展示这个问题更普遍)。
另一方面,如果您使用 Applicative
选择构图,您只需满足一个要求:
Future
和 Option
都应该在它们之上定义 Applicative
个实例
你不需要 Option
的任何特殊转换器,基本上猫库提供 Nested
class 适用于任何 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)
有效!
所以是的 Monad 是适用的,Option[Future[T]]
仍然是一个 monad(在 Future[T]
但不是 T
本身)但它允许你只使用 Future[T]
不是 T
。为了 "merge" Option
和 Future
层 - 你必须定义 monadic transformer FutureT
,为了合并 Future
和 Option
- 你必须定义 OptionT
。而且,OptionT
是在 cats/scalaz 中定义的,而不是 FutureT
。
一般(来自here):
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
所演示的那样,这种组合甚至不是必要的可交换(可交换)。
作为练习,你可以尝试定义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 中获取 "extract" 值,这在这里是不可能的,假设你不能从 Future
中提取值(没有定义 comonad ) 至少在 "non-blocking" 上下文中(如 ScalaJs)。这基本上意味着你不能 "swap" Future
和 F
,比如 Future[F[Future[B]] => F[Future[Future[B]
。后者是一个自然变换(函子之间的态射),所以这解释了对 this general answer:
的第一条评论
you can compose monads if you can provide a natural transformation swap : N M a -> M N a
Applicative
s 但是没有这样的问题 - 你可以很容易地组合它们,但请记住,两个 Applicatives
的组合结果可能不是 monad(但永远是一个应用程序)。 Nested[Future, Option, T]
不是 T
上的单子,尽管 Option
和 Future
都是 T
上的单子。简单来说 Nested as a class 没有 flatMap
.
阅读以下内容也会有所帮助:
- 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
是单子)
F[G[T]]
在 G[T]
上是一个 monad,但在 T
上不是
G_TRANSFORMER[F, T]
需要 F[G[T]]
才能在 T
上获得 monad。
- 没有
MEGA_TRANSFORMER[G, F, T]
因为这样的转换器不能构建在 monad 之上——它需要在 G
上定义额外的操作(似乎 G
上的 comonad 应该是够了)
- 每个 monad(包括
G
和 F
)都是 applicative,但不是每个 applicative 都是 monad
- 理论上
F[G[T]]
是对 G[T]
和 T
的应用。但是 scala 需要创建 NESTED[F, G, T]
才能在 T
上获得组合应用程序(在 cats 库中实现)。
NESTED[F, G, T]
是适用的,但不是 monad
这意味着你可以将 Future x Option
(又名 Option[Future[T]]
)组合成一个单子(因为 OptionT
存在),但你不能组合 Option x Future
(又名Future[Option[T]]
) 而不知道 Future 除了是 monad 之外还有别的东西(即使它们本质上是应用仿函数——应用不足以在其上构建 monad 或 monad 转换器)。基本上:
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]
;
您可以将任意两个应用组合成一个应用 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)。
当 Monad 是 Applicative 而 Applicative 是 Functor 时,为什么 monad 不组合。你在网络上的许多文章中看到了这个继承链(我已经经历过)。但是,当 Functor 和 Applicatives 组合时,为什么 Monad 会破坏它?
有人可以提供一个简单的 scala 示例来说明这个问题吗?我知道这被问了很多,但如果没有一个简单的例子就很难理解。
托尼·莫里斯 (Tony Morris) 发表了关于 monad 转换器的演讲,很好地解释了这个确切的问题。
http://tonymorris.github.io/blog/posts/monad-transformers/
他使用 haskell,但示例很容易翻译成 scala。
首先,让我们从一个简单的问题开始。比方说,我们需要得到两个整数的总和,每个整数都包含在 Future
和 Option
中。让我们使用 cats
库来类似于 Haskell 的标准库定义和 Scala 语法。
如果我们使用 monad 方法(又名 flatMap
),我们需要:
Future
和Option
都应该在它们之上定义Monad
个实例- 我们还需要 monadic 转换器
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
等
这里重要的是在cats中没有定义FutureT
,所以你可以写Future[Option[T]]
,但不能用Option[Future[T]]
来写(稍后我会展示这个问题更普遍)。
另一方面,如果您使用 Applicative
选择构图,您只需满足一个要求:
Future
和Option
都应该在它们之上定义Applicative
个实例
你不需要 Option
的任何特殊转换器,基本上猫库提供 Nested
class 适用于任何 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)
有效!
所以是的 Monad 是适用的,Option[Future[T]]
仍然是一个 monad(在 Future[T]
但不是 T
本身)但它允许你只使用 Future[T]
不是 T
。为了 "merge" Option
和 Future
层 - 你必须定义 monadic transformer FutureT
,为了合并 Future
和 Option
- 你必须定义 OptionT
。而且,OptionT
是在 cats/scalaz 中定义的,而不是 FutureT
。
一般(来自here):
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
所演示的那样,这种组合甚至不是必要的可交换(可交换)。
作为练习,你可以尝试定义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 中获取 "extract" 值,这在这里是不可能的,假设你不能从 Future
中提取值(没有定义 comonad ) 至少在 "non-blocking" 上下文中(如 ScalaJs)。这基本上意味着你不能 "swap" Future
和 F
,比如 Future[F[Future[B]] => F[Future[Future[B]
。后者是一个自然变换(函子之间的态射),所以这解释了对 this general answer:
you can compose monads if you can provide a natural transformation swap : N M a -> M N a
Applicative
s 但是没有这样的问题 - 你可以很容易地组合它们,但请记住,两个 Applicatives
的组合结果可能不是 monad(但永远是一个应用程序)。 Nested[Future, Option, T]
不是 T
上的单子,尽管 Option
和 Future
都是 T
上的单子。简单来说 Nested as a class 没有 flatMap
.
阅读以下内容也会有所帮助:
- 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
是单子)
F[G[T]]
在G[T]
上是一个 monad,但在T
上不是
G_TRANSFORMER[F, T]
需要F[G[T]]
才能在T
上获得 monad。- 没有
MEGA_TRANSFORMER[G, F, T]
因为这样的转换器不能构建在 monad 之上——它需要在G
上定义额外的操作(似乎G
上的 comonad 应该是够了) - 每个 monad(包括
G
和F
)都是 applicative,但不是每个 applicative 都是 monad - 理论上
F[G[T]]
是对G[T]
和T
的应用。但是 scala 需要创建NESTED[F, G, T]
才能在T
上获得组合应用程序(在 cats 库中实现)。 NESTED[F, G, T]
是适用的,但不是 monad
这意味着你可以将 Future x Option
(又名 Option[Future[T]]
)组合成一个单子(因为 OptionT
存在),但你不能组合 Option x Future
(又名Future[Option[T]]
) 而不知道 Future 除了是 monad 之外还有别的东西(即使它们本质上是应用仿函数——应用不足以在其上构建 monad 或 monad 转换器)。基本上:
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]
;您可以将任意两个应用组合成一个应用
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)。