2015-12-31 9 views
5

Wykorzystaliśmy transformator Monadera WriterT na platformie Futures, mając nadzieję na bardziej zorganizowane logi z aplikacji asynchronicznej, ale mieliśmy pewne problemy.Scalaz: jaką rolę odgrywa filtr w | @ |?

Jeśli skompiluję aplikację poniżej, pojawia się następujący błąd. Należy pamiętać, że nie jest to ostrzeżenie o filtrze withFilter.

[Błąd] Filtr wartość nie jest członkiem scalaz.WriterT [scala.concurrent.Future, List [ciąg], String]

Dlaczego | @ | potrzebujesz filtra tutaj? Czy Scalaz zapewnia niejawną konwersję dla tej sprawy?

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) } 

} 

Błąd występuje w wersji Scala: 2.11.7 i Scalaz wersja: 7.2.0

Odpowiedz

7

Często wygodny w użyciu biblioteki odbicie na reify aby zobaczyć, co się dzieje podczas desugaring (jest to jedyny raz, kiedy „d nigdy sugerować importowania czegokolwiek od scala.reflect.runtime, a nawet w tym przypadku tylko w REPL podczas rozwoju):

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))) 

problemem jest typu przypadek mecz wzór w dla - zrozumienie. Nawet jeśli kompilator zweryfikuje, czy dopasowanie jest bezpieczne (np. for { x: String <- Option(1) } yield x nie będzie się kompilować), nadal jest poddawane operacji filtrowania. Nie jestem pewien, dlaczego - prawdopodobnie jest ku temu powód, a jest nawet niewielka szansa, że ​​to dobry powód.

Otrzymujesz komunikat o filter, ponieważ jest to ostatnia deska kompilatora w tym przypadku. Najpierw spróbuje odwołać się do wywołania withFilter, a jeśli nie znajdzie withFilter, użyje filter (co najmniej w implementacjach biblioteki kolekcji jest generalnie mniej wydajne w takich przypadkach). W przypadku WriterT nie znajduje się ani (ponieważ filtrowanie pisarza w ogólnym przypadku nie ma sensu), więc narzeka na ostatnią próbę.

Rozwiązanie polega na tym, że nie należy używać dopasowywania wielkości liter. Napisz zwykły stary for { z <- ... } yield z i wszystko będzie działało dobrze. W rzeczywistości I'd suggest not ever using type-case matching - jest to najbardziej podstępny sposób rozwiązywania problemów związanych z odbiciem w środowisku wykonawczym, który kończy się w kodzie. W tym przypadku operacja zostanie sprawdzona podczas kompilacji (przynajmniej w oczywistych przypadkach), ale nadal jest niepotrzebna i potencjalnie mniej wydajna.

+1

Dodałem adnotację typu, aby pomóc typowi IntelliJ sprawdzić kod. Nigdy bym nie przypuszczał, że to może być szkodliwe. –

Powiązane problemy