2017-05-15 4 views
0

Ich habe zwei Funktionen mit der gleichen Implementierung - nur eine Handles Option und die anderen Handles Seq. Ich möchte Generics verwenden, um dies als eine einzige Funktion zu schreiben, die Iterable behandelt, während der konkrete Typ im aufrufenden Code beibehalten wird - wenn das möglich ist?Verwenden von Iterable mit Scala-Generika

def f[T](a: Seq[Failure \/ T]): Failure \/ Seq[T] = { ??? }
def g[T](b: Option[Failure \/ T]): Failure \/ Option[T] = { ??? }

Die Implementierung ist nicht wichtig, aber für Kontext sie aus einer Sammlung von Ergebnissen übersetzen (von denen jedes entweder gelungen (T) oder nicht (Failure)) entweder auf einen einzigen Fehler oder eine vollständige Sammlung von erfolgreichen Ergebnissen. \/ ist nur scalaz Version von Entweder.

Ich suche so etwas zu tun:
def f[I[T] <: Iterable[T]](results: I[Failure \/ T]): Failure \/ I[T] = { ??? }

Antwort

2

Von der Spitze meines Kopfes und nicht getestet, so entschuldigen Sie sich für Tippfehler.

So etwas, Sie haben eine innere Rekursion, die "Kurzschluss", wenn der erste Fehler gefunden wird.

1

Sie können etwas tun (siehe Implementierung für Future.sequence als Beispiel):

def f[T, M[X] <: Iterable[X]](results: M[Failure \/ T): Failure \/ M[T] 

Sie werden wahrscheinlich einige brauchen CanBuildFrom auch.

3

In FP wird dieses Muster durch ein Zusammenspiel zwischen einer traversable Sammlung (wie Seq oder Option) und einem applicative Funktors (wie Failure \/ ?) ausgedrückt.

Die generische Implementierung (mit scalaz) ist dann

import scalaz._ 
import scalaz.syntax.traverse._ 

def f[F[_]: Traverse, G[_]: Applicative, T](a: F[G[T]]): G[F[T]] = a.sequence 

Am Aufrufort, würden Sie

import scalaz.std._ 

type FailureOr[A] = Failure \/ A 

val x: Option[FailureOr[Int]] = ??? 
val y: List[FailureOr[Int]] = ??? 
val z: Vector[FailureOr[Int]] = ??? 

f[Option, FailureOr, Int](x) 
f[List, FailureOr, Int](y) 
f[Vector, FailureOr, Int](z) 

// or just directly 

import scalaz.syntax.traverse._ 

x.sequence 
y.sequence 
z.sequence 

Hinweis tun, die ich verwendet List und Vector statt Seq. Dies liegt daran, dass scalaz keine implizite Traverse Instanz für Seq bereitstellt. Während konzeptionell Seq verfahrbar ist, ist es aus Performance-Gründen besser, die Traverse Operationen speziell für konkrete Implementierungen von Seq wie List oder Vector zu implementieren. Wenn Sie wirklich wollen, können Sie Ihre eigene Instanz von Traverse[Seq] schreiben, nur wissen, dass es für einige Implementierungen von Seq suboptimal sein wird.