How to create a wrapper of List with a specific type

前端 未结 2 1258
慢半拍i
慢半拍i 2020-12-19 22:47

I am trying to create a wrapper of List with a specific type (e.g. List[Int]) such that methods that take an implicit CanBuildFrom par

相关标签:
2条回答
  • 2020-12-19 22:59

    As far I know from reading this article:

    http://www.scala-lang.org/docu/files/collections-api/collections-impl.html

    your solution is the simplest one, if you want filter/map/etc. to all return instances of MyList. newBuilder is needed for ops like filter, and the implicit CanBuildFrom for ops like map, which may change the collection type.

    What you should maybe do in your CanBuildFrom is this:

    def apply(from: MyList) = from.newBuilder // call it on `from'
    

    which ensures that a map on a statically-typed MyList which actually has a dynamic type that is a subtype of MyList will reuse that same dynamic type.

    Edit: seems like there is a little something missing, for this map returns an instance of List and not MyList:

    val l1: LinearSeq[Int] = new MyList(List(1, 2, 3))
    println(l1.map(_ + 1)) // prints List(2, 3, 4)
    

    it looks like this is also the case with the RNA example taken from the linked article. If it has static type IndexedSeq[Base] instead of RNA, a map on it returns a vector.

    Edit 2: looks like this is a more general problem, discussed in this question.

    0 讨论(0)
  • 2020-12-19 23:03

    Regarding my follow-up question how to mixin the wrapper logic via classes or traits, this is what I came up with:

    import scala.collection._
    
    trait ListWrapper[Elem, Repr <: ListWrapper[Elem, Repr]]
        extends immutable.LinearSeq[Elem]
        with LinearSeqLike[Elem, Repr]
        with generic.SeqForwarder[Elem] { self: Repr =>
    
      def wrapperCompanion: ListWrapperCompanion[Elem, Repr]
    
      override def newBuilder: mutable.Builder[Elem, Repr] =
        wrapperCompanion.newBuilder
    }
    
    trait ListWrapperCompanion[Elem, Repr <: ListWrapper[Elem, Repr]] {
      def apply(elems: TraversableOnce[Elem]): Repr
    
      def newBuilder: mutable.Builder[Elem, Repr] =
        new mutable.ListBuffer[Elem].mapResult(apply)
    
      def canBuildFromWrapper: generic.CanBuildFrom[Repr, Elem, Repr] = {
        new generic.CanBuildFrom[Repr, Elem, Repr] {
          def apply(from: Repr) = from.newBuilder
          def apply() = newBuilder
        }
      }
    }
    

    Now MyList can be implemented by:

    class MyList(val underlying: List[Int]) extends ListWrapper[Int, MyList] {
      def wrapperCompanion = MyList
    }
    
    object MyList extends ListWrapperCompanion[Int, MyList] {
      def apply(elems: TraversableOnce[Int]) = new MyList(elems.toList)
    
      implicit def canBuildFrom = canBuildFromWrapper
    }
    

    This is definitely better than having all the boilerplate code in MyList's definition, but it is still a lot to write for making MyList just a wrapper for List.

    0 讨论(0)
提交回复
热议问题