Sto provando a scrivere alcuni metodi di estensione per le collezioni Scala, e nei guai li sto generando completamente.Come si usa il membro del tipo A in IsTraversableLike?
Un primo tentativo di tailOption produce qualcosa come:
implicit class TailOption[A, Repr <: GenTraversableLike[A, Repr]](val repr: Repr) {
def tailOption: Option[Repr] =
if (repr.isEmpty) None
else Some(repr.tail)
}
purtroppo, questo non funziona:
scala> List(1,2,3).tailOption
<console>:19: error: value tailOption is not a member of List[Int]
List(1,2,3).tailOption
Scala 2.10 fornisce l'IsTraversableLike tipo di classe per aiutare adattare questo genere di cose per tutte le raccolte (comprese quelle dispari, come le stringhe).
Con questo può per esempio implementare tailOption abbastanza facilmente:
implicit class TailOption[Repr](val r: Repr)(implicit fr: IsTraversableLike[Repr]) {
def tailOption: Option[Repr] = {
val repr = fr.conversion(r)
if (repr.isEmpty) None
else Some(repr.tail)
}
}
scala> List(1,2,3).tailOption
res12: Option[List[Int]] = Some(List(2, 3))
scala> "one".tailOption
res13: Option[String] = Some(ne)
Il risultato è del tipo corretto: Option[<input-type>]
. In particolare, sono stato in grado di preservare il tipo Repr
quando si chiamano metodi che restituiscono Repr
, come `tail.
Purtroppo, non riesco a utilizzare questo trucco per preservare il tipo di elementi della collezione. Non riesco a chiamare metodi che restituiscono un elemento.
IsTraversableLike
ha un membro A, ma non sembra molto utile. In particolare non riesco a ricostruire il mio tipo di elemento originale e il membro non è equivalente nel tipo. Per esempio, senza ulteriore lavoro, headTailOption
assomiglia a questo:
implicit class HeadTailOption[Repr](val r: Repr)(implicit val fr: IsTraversableLike[Repr]) {
def headTailOption: Option[(fr.A, Repr)] = {
val repr = fr.conversion(r)
if (repr.isEmpty) None
else Some(repr.head -> repr.tail)
}
}
scala> val Some((c, _)) = "one".headTailOption
c: _1.fr.A forSome { val _1: HeadTailOption[String] } = o
Come possiamo vedere, C ha un tipo meravigliosamente barocco. Ma, questo tipo è non equivalente a Char:
scala> val fr = implicitly[IsTraversableLike[String]]
fr: scala.collection.generic.IsTraversableLike[String] = [email protected]
scala> implicitly[fr.A <:< Char]
<console>:25: error: Cannot prove that fr.A <:< Char.
implicitly[fr.A <:< Char]
Ho provato tutti i tipi di trucchi tra cui avere Repr[A] <: GenTraversableLike[A, Repr[A]]
nessuno dei quali aiutano. Chiunque può capire la salsa magica per rendere headTailOption
restituire i tipi giusti per:
val headTailString: Option[(Char, String)] = "one".headTailOption
val headTailList: Option[(Int, List[Int])] = List(1,2,3).headTailOption
Mi hai battuto. Sfortunatamente la suddivisione tra il metodo implicito che usa il tipo dipendente fr.A e la classe parametrizzata dal tipo è essenziale qui, quindi non credo che una soluzione di classe implicita sia (attualmente) possibile. –
Sì, sono giunto alla stessa conclusione ora. La classe implicita non è in grado di riprodurre la trasformazione/inferenza del tipo necessaria del tipo dipendente ... –