2017-09-09 2 views
4

Considérez ce qui suit travail sur mesure Seq:Comment créer un Seq personnalisé avec un paramètre de type bounded dans Scala?

class MySeq[B](val s: Seq[B]) 
extends Seq[B] 
with GenericTraversableTemplate[B, MySeq] 
with SeqLike[B, MySeq[B]] { 
    override def companion = MySeq 

    def iterator = s.iterator 

    def apply(i: Int) = s(i) 

    def length = s.length 

    override def toString = s map { _.toString } mkString("\n") 
} 
object MySeq extends SeqFactory[MySeq] { 
    implicit def canBuildFrom[B]: CanBuildFrom[Coll, B, MySeq[B]] = 
     new GenericCanBuildFrom[B] 
    def newBuilder[B] = new ListBuffer[B] mapResult (x => new MySeq(x.toSeq)) 
} 

je voudrais imposer une limite sur le paramètre de type B. En d'autres termes, je voudrais quelque chose comme ça (ne fonctionne pas):

class MyA 

class MySeq[+B <: MyA](val s: Seq[B]) 
extends Seq[B] 
with GenericTraversableTemplate[B, MySeq] 
with SeqLike[B, MySeq[B]] { 
    override def companion = MySeq // Type Mismatch Here 

    def iterator = s.iterator 

    def apply(i: Int) = s(i) 

    def length = s.length 

    override def toString = s map { _.toString } mkString("\n") 
} 
object MySeq extends SeqFactory[MySeq] { 
    implicit def canBuildFrom[B]: CanBuildFrom[Coll, B, MySeq[B]] = 
     new GenericCanBuildFrom[B] 
    // Type Mismatch in the line below 
    def newBuilder[B] = new ListBuffer[B] mapResult (x => new MySeq(x.toSeq)) 
} 

mais je reçois les erreurs d'incompatibilité de type suivantes dans les lignes indiquées:

inferred type arguments [B] do not conform to 
class MySeq's type parameter bounds [+B <: MyA] 
Main.scala line 49 

type mismatch; 
found : countvotes.structures.MySeq.type 
required: scala.collection.generic.GenericCompanion[Seq]  
Main.scala line 36 

type mismatch; 
found : MySeq[B(in class MySeq)] 
required: MySeq[B(in method newBuilder)]  
Main.scala line 49 

type mismatch; 
found : scala.collection.immutable.Seq[B(in method newBuilder)] 
required: Seq[B(in class MySeq)]  
Main.scala line 49 

J'ai essayé de résoudre ce problème en ajouter des bornes aux paramètres de type de CanBuildFrom et newBuilder, mais j'obtiens ensuite d'autres messages d'erreur.

Comment créer un Seq personnalisé avec un paramètre de type lié?

+1

Toute raison pour laquelle vous l'extension de la collection plutôt que l'encapsuler dans une classe d'emballage à l'exigence limite supérieure ? –

+0

La raison en est que ce serait plus élégant. J'utiliserai souvent 'map' et' filter' sur cette classe, et il serait peu pratique de déballer et de ré-emballer manuellement à chaque fois. – Bruno

Répondre

2

Je ne reçois pas l'erreur sur la ligne 26:

override def companion = MySeq 

Peut-être quelque chose d'autre est à l'origine que. De toute façon, le problème est que vous ne pouvez pas avoir un GenericCompanion[MySeq] (un supertype de SeqFactory). La raison est que GenericCompanion[Coll] signifie que vous pouvez construire un Coll[A] pour tout A (voir la signature de newBuilder). Vous ne pouvez pas non plus avoir MySeq[A] <: GenericTraversableTemplate[A, MySeq], car genericBuilder est impossible. C'est logique; MySeq n'est pas vraiment une "collection générique" car elle veut que ses éléments soient tous MyA. La solution est d'avoir MySeq[B] <: GenericTraversableTemplate[B, Seq], (qui est gratuit avec extends Seq). Vous avez ensuite deux choix pour companion. Il peut simplement être celui par défaut de Seq, ou il peut être s.companion. Dans le premier cas, ((as: MySeq[A]): Seq[A]).map(...) produira un List (à l'exécution, au compiletime c'est juste un Seq générique). Dans la seconde, cela dépendra de ce que as.s est (encore une fois, à l'exécution, compiletime ne verra qu'un Seq). Vous pouvez garder le extends SetLike, cependant. Ensuite, vous devez fournir un CanBuildFrom personnalisé: MySeq.canBuildFrom[A <: MyA]: CanBuildFrom[MySeq[A], A, MySeq[A]] et définir MySeq#newBuilder.

class MySeq[+B <: MyA](val s: Seq[B]) 
    extends Seq[B] 
    with SeqLike[B, MySeq[B]] 
{ 
    override def iterator = s.iterator 
    override def apply(i: Int) = s(i) 
    override def length = s.length 

    override def toString = s.map(_.toString).mkString("\n") 

    override def companion = s.companion 
    protected[this] override def newBuilder: mutable.Builder[B, MySeq[B]] = new mutable.Builder[B, MySeq[B]] { 
    private[this] val base = s.genericBuilder[B] 
    override def +=(elem: B) = { base += elem; this } 
    override def clear() = base.clear() 
    override def result() = new MySeq[B](base.result()) 
    } 
} 

object MySeq { 
    implicit def canBuildFrom[A <: MyA]: CanBuildFrom[MySeq[_], A, MySeq[A]] = ??? 
} 

val list = List(new MyA, new MyA, new MyA, new MyA) 
val vect = list.toVector 
val mLst = new MySeq(list) 
val mVec = new MySeq(vect) 
{ 
    val res = mLst.filter(_.hashCode != list.head.hashCode) 
    implicitly[res.type <:< MySeq[MyA]] 
} 
{ 
    val res = (mVec: Seq[MyA]).map(identity) 
    assert(res.isInstanceOf[Vector[_]]) 
} 
{ 
    val res = (mLst: Seq[MyA]).map(identity) 
    assert(res.isInstanceOf[List[_]]) 
} 
0

Voici une MWE complète basée sur la réponse acceptée de HTNW ci-dessus, avec canBuildFrom mis en œuvre:

class MyA 

class MySeq[+A <: MyA](val s: Seq[A]) 
    extends Seq[A] 
    with SeqLike[A, MySeq[A]] 
{ 
    override def iterator = s.iterator 
    override def apply(i: Int) = s(i) 
    override def length = s.length 

    override def toString = s.map(_.toString).mkString("\n") 

    override def companion = s.companion 
    protected[this] override def newBuilder = MySeq.newBuilder 
} 

object MySeq { 
    def newBuilder[A <: MyA] = new mutable.Builder[A, MySeq[A]] { 
    private[this] val base = Seq.newBuilder[A] 
    override def +=(elem: A) = { base += elem; this } 
    override def clear() = base.clear() 
    override def result() = new MySeq[A](base.result()) 
    } 

    implicit def canBuildFrom[A <: MyA]: CanBuildFrom[MySeq[_], A, MySeq[A]] = new CanBuildFrom[MySeq[_], A, MySeq[A]] { 
    def apply(from: Election[_]) = newBuilder 
    def apply() = newBuilder 
    } 
}