2015-03-21 15 views
15

Il seguenti bind(>>=) codice, a Haskell, non può essere compilato:Confronto Haskell e Scala Bind/Flatmap Esempi

ghci> [[1]] >>= Just 
<interactive>:38:11: 
    Couldn't match type ‘Maybe’ with ‘[]’ 
    Expected type: [t] -> [[t]] 
     Actual type: [t] -> Maybe [t] 
    In the second argument of ‘(>>=)’, namely ‘Just’ 
    In the expression: [[1]] >>= Just 

Ma, a Scala, lo fa in realtà compilare ed eseguire:

scala> List(List(1)).flatMap(x => Some(x)) 
res1: List[List[Int]] = List(List(1)) 

Haskell di >>= firma è:

>>= :: Monad m => m a -> (a -> m b) -> m b

Quindi, nel tipo [[1]] >>= f, f deve essere: a -> [b].

Perché compila il codice Scala?

risposta

19

Come spiegato @chi, lo flatMap di Scala è più generale di quello di Haskell >>=. La firma completa dalla documentazione Scala è:

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

Questa implicita non è rilevante per questo specifico problema, in modo da poter così utilizzare la definizione più semplice:

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

C'è solo un problema, Option non è una sottoclasse di GenTraversableOnce, qui entra in gioco una conversione implicita. Scala definisce una conversione implicita da Option a Iterable che è una sottoclasse di Traversable che è una sottoclasse di GenTraversableOnce.

implicit def option2Iterable[A](xo: Option[A]): Iterable[A] 

L'implicito è definito nell'oggetto associato di Option.

Un modo più semplice per vedere l'implicita sul lavoro è di assegnare un Option a un Iterableval:

scala> val i:Iterable[Int] = Some(1) 
i: Iterable[Int] = List(1) 

Scala usa alcune regole inadempienti, per selezionare List come l'attuazione di Iterable.

Il fatto che è possibile combinare diversi sottotipi di TraversableOnce con le operazioni monade viene dalla implicit classMonadOps:

implicit class MonadOps[+A](trav: TraversableOnce[A]) { 
    def map[B](f: A => B): TraversableOnce[B] = trav.toIterator map f 
    def flatMap[B](f: A => GenTraversableOnce[B]): TraversableOnce[B] = trav.toIterator flatMap f 
    def withFilter(p: A => Boolean) = trav.toIterator filter p 
    def filter(p: A => Boolean): TraversableOnce[A] = withFilter(p) 
    } 

Ciò aumenta ogni TraversableOnce con i metodi di cui sopra. I sottotipi sono liberi di definire versioni più efficienti in loco, che ombreggiano le definizioni implicite. Questo è il caso di List.

+0

E 'solo 'List's' flatMap' o funziona per ogni tipo in Scala? – Bergi

+0

Il 'GenTraversableOnce' è usato per tutte le" liste come "collezioni in Scala. È possibile utilizzare per es. un 'Vector' in combinazione con un' Elenco'. 'Vector (1) .flatMap (x => List (x)) == Vector (1)' o 'List (1) .flatMap (x => Vector (x)) == List (1)' – bmaderbacher

+1

Nah, Mi chiedevo se l'uso di 'GenTraversableOnce' nella firma' flatMap' è una generalizzazione specifica per gli elenchi o che funziona in tutte le monadi (tipi con un metodo 'flatMap'?)? – Bergi

10

Citando dal riferimento Scala List

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

Quindi, flatMap è più ampio di Haskell di (>>=), poiché richiede solo la funzione mappata f per generare un tipo traslabile, non necessariamente un List.

+0

dove vedo nella firma '(a -> mb)' che la funzione dovrebbe generare un tipo attraversabile, vedo solo che la funzione dovrebbe produrre 'mb' ma m è generale non è dove dice una lista? non è una lista denotata in haskell da '[]' non vedo alcun '[]' nella definizione di haskell bind ... – Jas

+1

@Jas Sopra sto solo dicendo che il bind di _for liste_ di Scala (mostrato sopra) è più generale di quello di Haskell _for lists_. Non sto confrontando il legame generale (Scala non ha legami "generali", solo le classi che hanno una flatMap, IIRC). In Haskell, abbiamo un list-bind '[a] -> (a -> [b]) -> [b]' quando in Scala è più simile a '[a] -> (a -> tb) - > [b] 'per un' t' adatto. – chi