ho creato un metodo di magnaccia, collate
, questo è utilizzabile da qualsiasi Traversable
o qualsiasi tipo che può essere costretto ad un percorribile secondo il seguente esempio:Semplificare le annotazioni di tipo
val ints = List(0,9,4,5,-3,-5,6,5,-2,1,0,6,-3,-2)
val results = ints collate {
case i: Int if(i < 0) => i.floatValue
} andThen {
case i: Int if(i>5) => i.toString
} andThen {
case i: Int if(i==0) => i
} toTuple
/*
results: (List[Float], List[java.lang.String], List[Int], List[Int]) =
(List(-3.0, -5.0, -2.0, -3.0, -2.0),List(9, 6, 6),List(0, 0),List(4, 5, 5, 1))
*/
pensare ad esso come l'empia uova di un'unione 'Twixt collect
e partition
, se si vuole ...
e' definita in questo modo:
import collection.generic.CanBuildFrom
class Collatable[Repr <% Traversable[T], T](xs: Repr) {
// Results handling stuff, bit like a poor-man's HList, feel free to skip...
trait Results {
def remainder: Repr
type Append[That] <: Results
def append[That](tup: (That, Repr)): Append[That]
def andThen[R, That](pf: PartialFunction[T, R])
(implicit
matchesBuilder: CanBuildFrom[Repr, R, That],
remainderBuilder: CanBuildFrom[Repr, T, Repr]
) = {
val more = (new Collatable[Repr,T](remainder)).collateOne[R,That](pf)
append(more)
}
}
case class Results9[M1,M2,M3,M4,M5,M6,M7,M8,M9](
m1: M1, m2: M2, m3: M3, m4: M4, m5: M5, m6: M6, m7: M7, m8: M8, m9: M9,
remainder: Repr)
extends Results {
implicit def toTuple = (m1, m2, m3, m4, m5, m6, m7, m8, m9, remainder)
def append[That](tup: (That, Repr)) = error("too many")
}
// ... skip a bit, still in results logic ...
case class Results2[M1,M2](
m1: M1, m2: M2, remainder: Repr)
extends Results {
implicit def toTuple = (m1, m2, remainder)
type Append[That] = Results3[M1,M2,That]
def append[That](tup: (That, Repr)) = Results3(m1, m2, tup._1, tup._2)
}
case class Results1[M1](matches: M1, remainder: Repr) extends Results {
implicit def toTuple = (matches, remainder)
type Append[That] = Results2[M1, That]
def append[That](tup: (That, Repr)) = Results2(matches, tup._1, tup._2)
}
// and now... Our feature presentation!
def collateOne[R, That](pf: PartialFunction[T, R])
(implicit
matchesBuilder: CanBuildFrom[Repr, R, That],
remainderBuilder: CanBuildFrom[Repr, T, Repr]
) = {
val matches = matchesBuilder(xs)
val remainder = remainderBuilder(xs)
for (x <- xs) if (pf.isDefinedAt(x)) matches += pf(x) else remainder += x
(matches.result, remainder.result)
}
def collate[R, That](pf: PartialFunction[T, R])
(implicit
matchesBuilder: CanBuildFrom[Repr, R, That],
remainderBuilder: CanBuildFrom[Repr, T, Repr]
): Results1[That] = {
val tup = collateOne[R,That](pf)
Results1(tup._1, tup._2)
}
}
object Collatable {
def apply[Repr, T](xs: Repr)(implicit witness: Repr => Traversable[T]) = {
new Collatable[Repr, T](xs)
}
}
implicit def traversableIsCollatable[CC[X] <: Traversable[X], A](xs: CC[A]) =
Collatable[CC[A], A](xs)
implicit def stringIsCollatable(xs: String) =
Collatable[String, Char](xs)
Concettualmente, non è poi così scoraggiante una volta capito come funziona lo standard CanBuildFrom
, ma sto scoprendo che è stato sopraffatto dalla norma, specialmente con gli impliciti.
So che potrei semplificare molto la logica di ResultX utilizzando una HList, ed è qualcosa che probabilmente farò, quindi quel pezzo di codice non mi preoccupa particolarmente.
so anche che avrei potuto fare la mia vita in modo significativo più facile se ero in grado di limitare Repr
come un sottotipo di Traversable
. Ma mi rifiuto di farlo, perché non può essere usato contro le stringhe. Allo stesso modo, vorrei anche evitare di forzare le funzioni parziali a restituire un sottotipo di T - anche se questo è meno preoccupante in quanto potrei sempre infrangere la mia logica in operazioni distinte di collazione e mappa.
Altro che riguarda è CanBuildFrom[Repr, T, Repr]
, che mi sembra di continuare a ripetere, e che oscura le cose importanti dal mio metodo di firme. Questo è qualcosa che ritengo possa essere definito solo una volta a livello di classe, ma non ho ancora trovato un modo per farlo funzionare.
Qualche idea?
Vorrei rinominare 'andThen' a' orElse'. –
Ad ogni modo, ho adorato questo metodo. –
@Daniel - Prenderò il nome sotto consiglio, anche se non sono convinto che 'orElse' dia il giusto senso di concatenare le operazioni insieme. La mia preferenza sarebbe 'then', che è già stata presa come parola chiave, e sono ancora indecisa su' + '. –