[英]Why do monads not compose in scala
當Monad是一個應用者而一個Applicative是一個Functor時,為什么monad不能編寫。 你在網上的許多文章中看到了這個繼承鏈(我已經經歷過)。 但是,當Functors和Applicatives組成為什么Monads打破了這個?
有人可以在scala中提供一個演示此問題的簡單示例嗎? 我知道這個問題很多,但沒有一個簡單的例子就很難理解。
首先,讓我們從一個簡單的問題開始。 比方說,我們需要得到兩個整數的總和,每個整數都包含在Future
和Option
。 讓我們拿cats
庫來使用Scala語法來模擬Haskell的標准庫定義。
如果我們使用monad方法(又名flatMap
),我們需要:
Future
和Option
都應該在它們上面定義Monad
實例 OptionT
變換器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
如果你看看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
,所以你可以FutureT
Future[Option[T]]
,但不能用Option[Future[T]]
來做(后來我會證明這個問題是均勻的更通用的)。
另一方面,如果您使用Applicative
選擇合成,則您只需要滿足一個要求:
Future
和Option
都應該有針對它們定義的Applicative
實例 你不需要任何特殊的變換器用於Option
,基本上貓庫提供適用於任何Applicative
Nested
類(讓我們忘記應用構建器的糖以簡化理解):
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
為了將Option
與Future
層“合並” - 您必須定義FutureT
變換器FutureT
,以便將Future
與Option
合並 - 您必須定義OptionT
。 而且, OptionT
在cats / scalaz中定義,但不是FutureT
。
一般來說(從這里 ):
不幸的是,我們真正的目標,monad的組成,更加困難。 ..事實上,我們實際上可以證明,在某種意義上,沒有辦法只使用兩個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),至少在“非阻塞”上下文中(像ScalaJs)。 這基本上意味着你不能“交換” Future
和F
,就像Future[F[Future[B]] => F[Future[Future[B]
。 后者是一種自然變換(仿函數之間的態射),因此這解釋了對這個一般答案的第一個評論:
如果你能提供一個自然的轉換交換,你可以組成monads:NM a - > MN a
Applicative
小號但是沒有這樣的問題-你可以很容易地編寫它們,但要記住的兩個組成導致Applicatives
可能不是一個單子(但將永遠是一個適用)。 Nested[Future, Option, T]
不是T
上的monad,無論Option
和Future
都是T
上的monad。 使用簡單的單詞嵌套為類沒有flatMap
。
閱讀:
把它們放在一起( F
和G
是單子)
F[G[T]]
是在單子G[T]
,但不能在T
G_TRANSFORMER[F, T]
才能從F[G[T]]
得到T
上的monad。 MEGA_TRANSFORMER[G, F, T]
因為這樣的變換器不能構建在monad之上 - 它需要在G
定義的額外操作(看起來G
comonad就足夠了) G
和F
)都是適用的,但不是每個應用都是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
存在),但你不能在不知道的情況下OptionT
Option x Future
(又名Future[Option[T]]
)除了作為monad之外,未來是另外的東西(即使它們本身就是應用的仿函數 - 應用程序還不足以構建monad或monad變換器)。 基本上:
OptionT
可以看作是非交換二元運算符,定義為OptionT: Monad[Option] x Monad[F] -> OptionT[F, T]; for all Monad[F], T; for some F[T]
OptionT: Monad[Option] x Monad[F] -> OptionT[F, T]; for all Monad[F], T; for some F[T]
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]
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]
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]
Nested: Applicative[F] x Applicative[G] -> Nested[F, G]; for all Applicative[F], Applicative[G], T; for 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)。
托尼莫里斯發表了關於monad變壓器的演講,很好地解釋了這個問題。
http://tonymorris.github.io/blog/posts/monad-transformers/
他使用haskell,但這些例子很容易翻譯成scala。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.