2017-09-15 29 views
1

我參加了scala odersky課程,並認爲Flatmap採用的參數作爲參數,需要Monad的元素並返回不同類型的monad。平面地圖如何在Scala中真正起作用

trait M[T] { 
    def flatMap[U](f: T => M[U]): M[U] 
} 

在單子M [T],函數的返回類型也是相同的單子,U可能是不同類型的參數。

但是我看到互聯網上的例子,其中函數返回一個完全不同的Monad。我的印象是,返回類型的函數應該是相同的Monad。有人可以簡化下面的內容來解釋flapmap如何得到實際值而不是列表中的選項。

該列表不是Scala中的Monad。

val l= List(1,2,3,4,5) 
def f(x:int) = if (x>2) Some(x) else None 
l.map(x=>f(x)) 
//Result List[Option[Int]] = List(None , None , Some(3) , Some(4) , Some(5)) 
l.flatMap(x=>f(x)) 
//Result: List(3,4,5) 
+0

令人作嘔的話,你可以把幾乎任何你想要的簽名到'flatmap', 'map'和'filter',仍然使用'for'解析。 – Alec

回答

1

嗯,也許是混淆的,你給簽名實際上不是正確的,因爲它是真的(以簡化形式):

def flatMap[B](f: (A) ⇒ GenTraversableOnce[B]): Traversable[B] 

由於編譯器是開源的,你可以看到什麼它在做什麼(其完整的簽名):

def flatMap[B, That](f: A => GenTraversableOnce[B])(implicit bf: CanBuildFrom[Repr, B, That]): That = { 
    def builder = bf(repr) // extracted to keep method size under 35 bytes, so that it can be JIT-inlined 
    val b = builder 
    for (x <- this) b ++= f(x).seq 
    b.result 
} 

所以你可以看到,實際上是有沒有要求,即f返回類型是一樣的0123的返回類型。

+1

沒有必要查看編譯器源代碼,這在發佈Scaladoc中記錄了關於Scala庫 – cchantep

1

在標準庫中發現的flatmap比像flatMap從Odersky的的示例中的一元綁定方法的更全面和靈活的方法。

例如,flatmap的名單上的全部簽名

def flatMap[B, That](f: (A) ⇒ GenTraversableOnce[B])(implicit bf: CanBuildFrom[List[A], B, That]): That 

,而不是要求傳遞到flatmap函數返回一個列表,它能夠返回任何GenTraversableOnce對象,一個非常通用的類型。

flatmap然後使用隱式CanBuildFrom機制來確定要返回的適當類型。

因此,當您使用flatmap和一個返回List的函數時,它是一種monadic綁定操作,但它也允許您使用其他類型。

+0

'List'形成了一個完美的monad。僅僅因爲你可以做比正常單子更多的*並不妨礙它成爲一個單子。限制'flatMap'返回'List'完全正確,並生成基本的一元綁定。否則的話,單調綁定*通常*具有名稱'flatMap'的事實不應該足以使List不能僅僅因爲它的綁定操作被命名爲不同的名稱而使其成爲monad。如果存在一些特徵Monadic [M [A] <:Monadic [M]] {def flatMap(f:A => M [B]):B}',你可以說'List'不是'Monadic',但是說這意味着它不是一個單子。 – HTNW

2

讓我們從M[T]本身不是monad的事實入手。它是一個類型構造函數。當它與兩個運營商關聯時,它將變成單子:bindreturn(或unit)。還有這些運營商必須滿足的單子法,但爲簡潔起見,我們將其省略。在Haskell的bind類型是:

class Monad m where 
    ... 
    (>>=) :: m a -> (a -> m b) -> m b 

m哪裏是一種類型的構造。由於Scala是面向對象的語言bind看起來像(第一個參數是個體經營):

trait M[T] { 
    def bind[U](f: T => M[U]): M[U] 
} 

這裏M === mT === aU === bbind通常被稱爲flatMap。在真空中的純球形世界中,將以OO語言簽名爲flatMap。Scala是一種非常實用的語言,所以flatMapList真正的簽名是:

final def flatMap[B, That](f: (A) ⇒ GenTraversableOnce[B])(implicit bf: CanBuildFrom[List[A], B, That]): That 

這不是bind,但將作爲一個單子bind工作,如果你提供f(A) => List[B]形式,也確保ThatList[B]。另一方面,如果您提供了不同的內容,Scala不會注視您的背部,但會嘗試找到一些有意義的轉換(例如CanBuildFrom或其他),如果存在的話。

UPDATE

你可以用scalac標誌(-Xlog-implicits-Xlog-implicit-conversions)玩,看看發生了什麼:

scala> List(1).flatMap { x => Some(x) } 
<console>:1: inferred view from Some[Int] to scala.collection.GenTraversableOnce[?] via scala.this.Option.option2Iterable[Int]: (xo: Option[Int])Iterable[Int] 
     List(1).flatMap { x => Some(x) } 
           ^
res1: List[Int] = List(1) 
相關問題