2017-06-27 2 views
2

Ich möchte eine Methode schreiben, die None zurückgibt, wenn eine Sammlung leer ist und Some(collection) in anderen Fällen.Wie kann man eine generische implizite Klasse für jede Sammlung in Scala erstellen?

Best I erhalten können, ist

implicit class CollectionExtensions[A, Repr](self: TraversableLike[A, Repr]){ 
    def toOption: Option[Repr] = if (self.nonEmpty) Some(self.asInstanceOf[Repr]) else None 
} 

Aber .asInstanceOf[Repr] scheint Gießen falsch. Was ist der richtige Weg?

Antwort

4

Einige andere Ansätze sind Sie zusätzlich zu @ dk14 Antwort verwenden können:

  • Verwenden Sie eine implizite Klasse für Repr with TraversableOnce[A]. Dies unterstützt auch Iterator, weil IteratorTraversableOnce, aber nicht TraversableLike verlängert.

    implicit class CollectionExtensions[A, Repr](val self: Repr with TraversableOnce[A]) extends AnyVal { 
        def toOption: Option[Repr] = if (self.nonEmpty) Some(self) else None 
    } 
    
  • eine implizite Klasse Verwenden Sie nur für Repr, aber Beweise verlangen, dass es zu Traversable implizit konvertierbar ist. Dieser Ansatz unterstützt auch Array s und String s, weil sie nicht Traversable überhaupt erweitern, aber implizit umsetzbar sind.

    implicit class CollectionExtensions[Repr](val self: Repr) extends AnyVal { 
        def toOption[A](implicit ev: Repr => TraversableOnce[A]): Option[Repr] = { 
        val traversable = ev(self) 
        if (traversable.isEmpty) None else Some(self) 
        } 
    } 
    

den Originaltyp Diese beiden Ansätze erhalten:

scala> List(1, 2, 3).toOption 
res1: Option[List[Int]] = Some(List(1, 2, 3)) 

scala> Iterator(1, 2, 3).toOption 
res2: Option[Iterator[Int]] = Some(non-empty iterator) 

scala> Array.empty[Int].toOption 
res3: Option[Array[Int]] = None 

scala> Map(1 -> 2).toOption 
res4: Option[scala.collection.immutable.Map[Int,Int]] = Some(Map(1 -> 2)) 

scala> "123".toOption 
res5: Option[String] = Some(123) 

scala> "".toOption 
res6: Option[String] = None 
3

Um ursprünglichen Repr Art zu erholen, können Sie self.repr (scaladoc)

implicit class CollectionExtensions[A, Repr](self: TraversableLike[A, Repr]){ 
    def toOption: Option[Repr] = if (self.nonEmpty) Some(self.repr) else None 
} 

verwenden Wenn Sie nur mit Option[TraversableLike[A, Repr]] wie @ chengpohi halten würde die Antwort schon sagt, Operationen wie map darauf (list.toOption.map(_.map(x => x))) würden Sie zurückkehren Option[Traversable[T]] Verlust der ursprünglichen Repr Art (wie List[Int]). repr hilft mit, dass:

def repr: Repr

Die Sammlung von Typ verfahrbaren Sammlung dieses TraversableLike Objekt zugrunde liegt. Standardmäßig ist dies als das TraversableLike-Objekt selbst implementiert, aber dies kann überschrieben werden.


jedoch das Komische ist, wenn man sich repr ‚s Code aussehen (here):

def repr: Repr = this.asInstanceOf[Repr] 

Es tut gleiche Sache, aber zumindest wickelte es (versteckt?) Schön in scala-library und abstrakter, so könnten Sie potenzielle Neudefinitionen berücksichtigen.


Auch ist es erwähnenswert, dass diese nicht mit leerer Sammlung Ansatz in scalaz/Katzen beliebt ist:

scala> import scalaz._; import Scalaz._ 
import scalaz._ 
import Scalaz._ 

scala> List(1, 2, 3).toNel 
res8: Option[scalaz.NonEmptyList[Int]] = Some(NonEmptyList(1, 2, 3)) 

scala> nil[Int].toNel 
res9: Option[scalaz.NonEmptyList[Int]] = None 

toNel bedeutet hier toNonEmptyList, es ist also nicht so abstrakt, wie Ihre Lösung. Katzen haben OneAnd[A,Repr] und einige hilfreiche implicits. Siehe http://typelevel.org/cats/datatypes/oneand.html

Verwandte Themen