2015-12-31 22 views
5

Abbiamo utilizzato il trasformatore monad WriterT su Futures sperando di ottenere registri più organizzati da un'applicazione asincrona, ma abbiamo incontrato dei problemi.Scalaz: che ruolo gioca il filtro in | @ |?

Se compilo l'applicazione di seguito, ottengo il seguente errore. Si prega di notare che questo non è l'avviso su withFilter.

[errore] Filtro valore non è un membro del scalaz.WriterT [scala.concurrent.Future, List [String], String]

Perché | @ | bisogno di filtro qui? Scalaz fornisce una conversione implicita per questo caso?

import scala.concurrent.ExecutionContext.Implicits.global 
import scala.concurrent.Future 
import scalaz._ 
import Scalaz._ 

object Failure extends App { 

    type LoggedT[F[_], A] = WriterT[F, List[String], A] 
    type LoggedFuture[A] = LoggedT[Future, A] 

    //this is the applicative behavior that we want WriterT to preserve 
    val example = for { 
    z <- (Future("left") |@| Future("right")) { (x: String, y: String) => x + " " + y } 
    } yield z 

    example.onSuccess { case x => println(x) } 

    val test = for { 
    z: String <- (Future("left").liftM[LoggedT] |@| Future("right").liftM[LoggedT]) { (x: String, y: String) => x + " " + y } 
    } yield z 

    test.value.onSuccess { case x => println(x) } 

} 

L'errore si verifica con la versione Scala: 2.11.7 e Scalaz versione: 7.2.0

risposta

7

E 'spesso conveniente utilizzare la libreria di riflessione del reify per vedere cosa succede durante Dezuccheraggio (questa è l'unica volta che ho 'd mai suggerire l'importazione di qualsiasi cosa, da scala.reflect.runtime, e anche in questo caso solo nel REPL durante lo sviluppo):

scala> import scala.reflect.runtime.universe.reify 
import scala.reflect.runtime.universe.reify 

scala> reify(for { x: Int <- Option(1) } yield x) 
res5: reflect.runtime.universe.Expr[Option[Int]] = 
Expr[scala.Option[Int]](Option.apply(1).withFilter(((check$ifrefutable$1) => check$ifrefutable$1: @unchecked match { 
    case (x @ (_: Int)) => true 
    case _ => false 
})).map(((x: Int) => x))) 

il problema è il pattern match di tipo caso in per -comprensione. Anche se il compilatore verificherà che la corrispondenza sia sicura (ad esempio, for { x: String <- Option(1) } yield x non verrà compilata), verrà comunque eseguita l'operazione di filtro. Non sono sicuro del perché - c'è probabilmente una ragione per questo, e c'è anche una piccola possibilità che sia una buona ragione.

Si ottiene un messaggio su filter perché in questo caso è l'ultima risorsa del compilatore. Innanzitutto tenterà di eseguire una chiamata a withFilter e, se non trova uno withFilter, utilizzerà filter (che, almeno nelle implementazioni della raccolta di raccolte, è generalmente meno efficiente in casi come questo). Nel caso di WriterT non trova né (dal momento che il filtro di uno scrittore non ha senso nel caso generale), quindi si lamenta dell'ultimo tentativo.

La soluzione non è quella di utilizzare la corrispondenza di maiuscole/minuscole. Scrivi un semplice vecchio for { z <- ... } yield z e tutto funzionerà bene. Infatti, I'd suggest not ever using type-case matching, è il modo più snello per terminare nel tuo codice i problemi relativi alla riflessione in fase di esecuzione. In questo caso l'operazione verrà verificata in fase di compilazione (almeno nei casi ovvi), ma non è ancora necessaria e potenzialmente meno efficiente.

+1

Ho aggiunto l'annotazione del tipo solo per aiutare il tipo IntelliJ a controllare il codice. Non avrei mai immaginato che potesse essere così dannoso. –