为什么Monad无法在Scala中编写 [英] Why do monads not compose in scala

查看:66
本文介绍了为什么Monad无法在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.

推荐答案

首先,让我们从一个简单的问题开始.假设我们需要得到两个整数的和,每个整数都包裹在FutureOption中.让我们使用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:

  • FutureOption都应在其上定义Monad个实例
  • 我们还需要一元变压器OptionT,该变压器仅适用于Option(确切地说是F[Option[T]])
  • both Future and Option should have Monad instances defined over them
  • we also need monadic transformer OptionT which will work only for Option (precisely F[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的内部逻辑和结构(foldNone). EitherTStateT等的相同问题.

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:

  • FutureOption都应在其上定义Applicative个实例
  • both Future and Option should have Applicative instances defined over them

Option不需要任何特殊的转换器,基本上cats库提供了适用于任何ApplicativeNested类(为简化理解,请忽略应用构建器的糖):

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进行操作.为了将OptionFuture层合并"-您必须定义单变变压器FutureT,为了将FutureOption合并-您必须定义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

而且,正如我为OptionFuture演示的那样,此组合甚至不是必需的可交换(可交换).

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]一样交换" FutureF.后者是自然变换(函子之间的同构),因此可以解释对此一般答案的第一个评论:

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上的单子,无论OptionFuture都是T上的单子.用简单的文字嵌套为类没有flatMap.

Applicatives 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

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

Putting it all together (F and G are monads)

  • F[G[T]]G[T]上的单子,但不是T
  • 上的单子. 要从F[G[T]]获得T上的monad,需要
  • G_TRANSFORMER[F, T].
  • 没有MEGA_TRANSFORMER[G, F, T],因为这样的转换器不能在monad之上构建-它需要在G上定义的其他操作(看来G上的comonad应该足够了)
  • 每个monad(包括GF)都是可应用的,但并非每个可应用的对象都是一个monad
  • 理论上,F[G[T]]G[T]T的适用语.但是,scala需要创建NESTED[F, G, T]才能在T上组合使用(在cats库中实现).
  • NESTED[F, G, T]适用,但不是monad
  • F[G[T]] is a monad on G[T], but not on T
  • G_TRANSFORMER[F, T] required in order to get a monad on T from F[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 on G (it seems like comonad on G should be enough)
  • every monad (including G and F) is applicative, but not every applicative is a monad
  • in theory F[G[T]] is an applicative over both G[T] and T. However scala requires to create NESTED[F, G, T] in order to get composed applicative on T (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 as OptionT: 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆