2014-06-21 10 views
6

seguente è un esempio di giocattolo per dimostrare la stranezza e il punto della questione dei metodi di vita reale.Appiattisci i tipi dopo aver composto due errori

Come si può vedere anotherFunc, dopo la mappatura su personList espande tipo di \/[Throwable,List[\/[Throwable,String]]] che non è destinato tipo di ritorno, ma effetto di map ing personList. ancora una volta che cosa è illustrato di seguito all'interno anotherFunc è a scopo dimostrativo (in realtà non c'è cose più significative che accadono invece di Option("fakeString") o nulla di tutto ciò.

requisito è quello di mappersonList e se è right poi fare qualcosa con ogni elemento del List[Person] restituita da right (lato che è stato restituito dalla disgiunzione).

come semplificare/appiattire tipo di ritorno di anotherFunc (per tornare \/[Throwable,List[String]]). Può essere utilizzando altre combinatori?

case class Person(name :String) 

def personList : \/[Throwable,List[Person]] ={ 
    \/.fromTryCatch{ 
    List(Person("John")) 
    } 
} 

def anotherFunc : \/[Throwable,List[\/[Throwable,String]]]= { 
    personList.map{ pl => 
    pl.map{p => 
     for{ 
     s <- Option("fakeString").\/>(new Throwable("not found")) 
     } yield s 

    } 

    } 
} 

risposta

8

risposta di Noè è fondamentalmente giusto, ma è necessario utilizzare sempre traverse invece di un map seguito da un sequence -la due sono equivalenti, ma il primo è più chiara e un po 'più efficiente:

def anotherFunc: Throwable \/ List[String] = personList.flatMap { pl => 
    pl.traverseU { p => 
    for { 
     // I assume you're doing something more interesting here... 
     s <- Option("fakeString").\/>(new Throwable("not found")) 
    } yield s 
    } 
} 

sto facendo questo una risposta invece di un commento, però, perché c'è un altro modo per risolvere questo tipo di problema che può essere più elegante in alcune situazioni. Se stai facendo un sacco di lavoro con le liste di disgiunzioni, è possibile utilizzare il trasformatore ListT Monade per far sembrare che hai a che fare con un solo livello:

type OrThrowable[A] = Throwable \/ A  

def personList = ListT[OrThrowable, Person](
    \/.fromTryCatch { List(Person("John")) } 
) 

def anotherFunc: ListT[OrThrowable, String] = personList.flatMap { p => 
    Option("fakeString").\/>(new Throwable("not found")).liftM[ListT] 
} 

Ora basta utilizzare anotherFunc.run alla fine per prendi un Throwable \/ List[Person] e questo è esattamente equivalente al tuo codice attuale (ma molto più conciso).

+0

Non stavo nemmeno pensando a 'traverseU', buona risposta! – Noah

+1

Grazie, @Noah, e mi dispiace per lo scoop-se "traverse" era tutto ciò che stavo aggiungendo avrei fatto un commento. –

5

Se flatMap il personList e poi sequenceU l'elenco interno si può sostanzialmente flatten vostro tipo di ritorno:

def anotherFunc: \/[Throwable, List[String]] = { 
    personList.flatMap(
     pl => 
     pl.map(
      p => 
      for { 
       s <- Option("fakeString").\/>(new Throwable("not found")) 
      } yield s 
     ).sequenceU 
    ) 
    } 

Intellij lamenta con alcune linee rosse, ma si compila e stampa correttamente per me \/-(List(fakeString)).

Questa versione sembra un po 'più bello a mio parere:

def anotherFunc2: \/[Throwable, List[String]] = { 
    for { 
     pl <- personList 
     res <- pl.map(p => Option("fakeString").\/>(new Throwable("not found"))).sequenceU 
    } yield res 
    } 
Problemi correlati