为什么monad不会在scala中构成

Jay*_*Jay 16 monads scala higher-kinded-types

当Monad是一个应用者而一个Applicative是一个Functor时,为什么monad不能编写.你在网上的许多文章中看到了这个继承链(我已经经历过).但是,当Functors和Applicatives组成为什么Monads打破了这个?

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

dk1*_*k14 18

首先,让我们从一个简单的问题开始.比方说,我们需要得到两个整数的总和,每个包裹在两FutureOption.让我们使用cats库来使用Scala语法来模拟Haskell的标准库定义.

如果我们使用monad方法(aka flatMap),我们需要:

  • 双方FutureOptionMonad在他们定义的实例
  • 我们还需要monadic变压器OptionT,它只适用于Option(精确F[Option[T]])

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

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
Run Code Online (Sandbox Code Playgroud)

如果你看看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)))
Run Code Online (Sandbox Code Playgroud)

您会注意到代码非常特定于Option内部逻辑和结构(fold,None).同样的问题EitherT,StateT等等

这里重要的是,FutureT猫没有定义,所以你可以编写Future[Option[T]],但不能这样做Option[Future[T]](后来我会证明这个问题更通用).

另一方面,如果您选择使用合成Applicative,您只需要满足一个要求:

  • 双方FutureOptionApplicative在他们定义的实例

您不需要任何特殊的变换器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)
Run Code Online (Sandbox Code Playgroud)

让我们交换选项和未来:

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)
Run Code Online (Sandbox Code Playgroud)

作品!

所以是Monad是Applicative,Option[Future[T]]仍然是一个monad(on Future[T]但不是它T自己)但它允许你只运行Future[T]not T.为了"合并" OptionFuture图层-你必须定义单子变压器FutureT,以合并FutureOption-你必须定义OptionT.并且,OptionT在cat/scalaz中定义,但不是FutureT.

一般来说(从这里):

不幸的是,我们真正的目标,monad的组成,更加困难...事实上,我们实际上可以证明,在某种意义上,没有办法只使用两个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
   })
Run Code Online (Sandbox Code Playgroud)

基本上这种实现的问题是你必须从r中"提取"值,这在这里是不可能的,假设你不能Future至少在"非阻塞"上下文中从(没有定义的comonad)中提取值(像ScalaJs).这基本上意味着你不能"交换" FutureF一样Future[F[Future[B]] => F[Future[Future[B].后者是一种自然的转换(仿函数之间的态射),所以这解释了对这个一般答案的第一个评论:

如果你能提供一个自然的转换交换,你可以组成monads:NM a - > MN a

Applicative然而,没有这样的问题 - 你可以很容易地组成它们,但要记住,两个人的组成结果Applicatives可能不是一个单子(但总是一个应用).Nested[Future, Option, T]是不是一个单子T,不管双方OptionFuture都在单子T.插入简单的单词嵌套为类没有flatMap.

阅读:

把它们放在一起(F并且G是monad)

  • F[G[T]]是monad G[T],但不是T
  • G_TRANSFORMER[F, T]为了得到一个单子上需要TF[G[T]].
  • 没有,MEGA_TRANSFORMER[G, F, T]因为这样的变换器不能构建在monad之上 - 它需要额外的操作定义G(似乎comonad on G应该就够了)
  • 每个monad(包括GF)都是适用的,但不是每个应用都是monad
  • 从理论上讲F[G[T]]是在两个一个适用G[T]T.但是scala需要创建NESTED[F, G, T]才能获得组合应用T(在cat库中实现).
  • NESTED[F, G, T] 是适用的,但不是monad

这意味着你可以组成Future x Option(也就是Option[Future[T]])一个单一的monad(coz OptionT存在),但是你不能在不知道Future是除了monad之外的其他东西的情况下编写Option x Future(aka Future[Option[T]])(尽管它们本身就是应用函数 - 应用程序不是足以既不建立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(固有的functor)组成一个applicative(但不是monad).