2015-10-15 62 views
6

爲什麼單聲道不是monad是一個應用程序,而application是一個functor。你在網絡上的許多文章中看到這個繼承鏈(我經歷過)。但是,當遊戲者和應用者撰寫Monad爲什麼打破這個?monad爲什麼不在scala中編寫

有人可以在scala中提供一個簡單的例子來演示這個問題嗎?我知道這個問題很多,但沒有一個簡單的例子就很難理解。

+0

可能的重複[Applicatives撰寫,單子不要](http://stackoverflow.com/questions/7040844/applicatives-compose-monads-dont) – ziggystar

+0

這是haskell答案不適用於scala – Jay

+4

檢查出[答案由Conal](http://stackoverflow.com/a/7070339/108915)。它是語言不可知的。 Monad是一個數學概念,它們在語言之間沒有區別。 – ziggystar

回答

9

首先,我們從一個簡單的問題開始。比方說,我們需要得到兩個整數的總和,這兩個整數都包含在FutureOption中。我們假設,我們使用cats庫。

如果我們用單子辦法(又名flatMap),我們需要:

  • FutureOption應該對他們定義Monad實例
  • 我們還需要一元變壓器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的內在邏輯和結構(foldNone)。同樣的問題對於EitherTStateT

這裏

重要的是,有沒有貓定義FutureT,這樣你就可以撰寫Future[Option[T]],但不能做到這一點與Option[Future[T]](以後我會告訴這個問題甚至更通用)。

在另一方面,如果你選擇使用Applicative組成,你必須滿足只有一個要求:

  • FutureOption應該有Applicative情況下對他們

定義你不需要任何特殊的變形器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) 

讓我們交換他們:

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) 

作品!

所以,是的單子是應用型,Option[Future[T]]仍然是一個單子(上Future[T]但不能在T本身),但它可以讓你只操作Future[T]T。爲了「合併」OptionFuture層 - 您必須定義一元變換器FutureT,爲了合併FutureOption - 您必須定義OptionT。並且,OptionT在cats/scalaz中定義,但不是FutureT

一般(從here):

不幸的是,我們真正的目標,單子組成,是相當多 困難。實際上,我們實際上可以證明,從某種意義上說, 沒有辦法構造一個上述類型的連接函數,只能使用兩個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 
    }) 

基本符合這樣的實現問題是,你必須從r這是不可能在這裏‘提取’的價值,假設你可以不提取Future的值(沒有定義它的comonad),如果我們正在討論「非阻塞」的API,這是真的。這基本上意味着你不能「交換」FutureF,就像Future[F[Future[B]] => F[Future[Future[B]那樣是自然變換(仿函數之間的態射)。這樣解釋了this general answer第一個註釋:

您可以撰寫單子,如果你能提供一個自然轉化交換:納米 - >明尼蘇達州一個

Applicative小號但是沒有這樣的問題 - 你可以很容易地編寫它們,但請記住,兩個Applicatives的組合結果可能不是單子(但將永遠是一個應用)。 Nested[Future, Option, T]不是T上的monad,無論是Option還是Future都是T上的monad。用簡單的詞彙Nested as a class沒有flatMap

這將是也有利於閱讀:

全部放在一起(FG是單子)

  • F[G[T]]G[T]一個單子,但不是在T
  • G_TRANSFORMER[F, T]爲了從F[G[T]]得到T單子需要。
  • 沒有MEGA_TRANSFORMER[G, F, T]因此變壓器不能建立在單子上面 - 它需要G定義的附加操作(好像在G comonad應該足夠)
  • 每一個單子(包括GF)被但不是每一個應用都是單粒子
  • 理論上F[G[T]]適用於G[T]T。然而,斯卡拉需要創建NESTED[F, G, T]爲了得到T(它是在貓庫中實現)組成應用。
  • NESTED[F, G, T]是合用的,但沒有一個單子

這意味着你可以撰寫Future x Option(又名Option[Future[T]])到一個單一的單子,但你不能在不知道撰寫Option x Future(又名Future[Option[T]]),他們是什麼除了單子之外。你可以將任何兩個應用程序組合成一個應用程序,你也可以將任意兩個monad組合成一個單一的應用程序(但不是一個單一的monad)。

0

託尼莫里斯就單子變壓器進行了一次演講,這很好地解釋了這個準確的問題。

http://tonymorris.github.io/blog/posts/monad-transformers/

他用哈斯克爾,但實例容易翻譯到階。

+0

我想避免這個haskell語法,因爲它更難以閱讀,特別是對於這個主題的初學者!所以我更喜歡簡單的scala示例,這是可以理解的。 Thx – Jay

+1

也許你應該爲Functors編寫一個組合函數,然後嘗試爲Monads寫一個函數 - 它會給你一個很好的見解。 – melps

+2

這是一個很好的資源,對相關部分的總結會是一個很好的答案,但是對於自己的外部資源的鏈接應該是評論,而不是答案。 –

相關問題