Как создать обертку List с определенным типом - PullRequest
3 голосов
/ 14 апреля 2011

Я пытаюсь создать оболочку List определенного типа (например, List[Int]), чтобы методы, которые принимают неявный параметр CanBuildFrom, возвращали экземпляр моей оболочки вместо List.

Одним из возможных решений, которое кажется довольно тяжелым, является:

import scala.collection._
import generic.{CanBuildFrom, SeqForwarder}
import mutable.{Builder, ListBuffer}

class MyList(list: List[Int]) extends immutable.LinearSeq[Int]
                                 with LinearSeqLike[Int, MyList]
                                 with SeqForwarder[Int] {
  override def newBuilder: Builder[Int, MyList] = MyList.newBuilder
  protected override def underlying = list
}

object MyList {
  def newBuilder: Builder[Int, MyList] =
    new ListBuffer[Int] mapResult(new MyList(_))

  implicit def canBuildFrom: CanBuildFrom[MyList, Int, MyList] = {
    new CanBuildFrom[MyList, Int, MyList] {
      def apply(from: MyList) = from.newBuilder
      def apply() = newBuilder
    }
  }
}

val l1 = new MyList(List(1,2,3))

println(l1.isInstanceOf[MyList])
println(l1.map(_ + 1).isInstanceOf[MyList])
println(l1.filter(_ == 2).isInstanceOf[MyList])

Есть ли лучший / более простой способ создать такую ​​обертку, или я упустил что-то важное в реализации MyList?

Редактировать: Следующий вопрос: можно ли всю эту логику обертки поместить в ListWrapper классы или признаки, чтобы можно было реализовать описанную выше MyList следующим образом:

class MyList extends ListWrapper[Int, MyList]

object MyList extends ListWrapperFactory[Int, MyList]

Ответы [ 2 ]

4 голосов
/ 14 апреля 2011

Насколько я знаю из прочтения этой статьи:

http://www.scala -lang.org / доку / файлы / сборники-апи / коллекции-impl.html

ваше решение самое простое, если вам нужен фильтр / карта / и т.д. для всех возвращаемых экземпляров MyList. newBuilder необходим для операций типа filter, а неявный CanBuildFrom для операций типа map, что может изменить тип коллекции.

Что вы, возможно, должны сделать в своем CanBuildFrom, это:

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

, который гарантирует, что map для статически типизированного MyList, который на самом деле имеет динамический тип, который является подтипом MyList, будет повторно использовать этот же динамический тип.

Редактировать : кажется, что чего-то не хватает, для этого map возвращает экземпляр List, а не MyList:

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

похоже, что это также относится к примеру RNA, взятому из связанной статьи. Если он имеет статический тип IndexedSeq[Base] вместо RNA, map возвращает вектор.

Редактировать 2 : похоже, это более общая проблема, обсуждаемая в в этом вопросе .

1 голос
/ 17 апреля 2011

Что касается моего последующего вопроса о том, как смешивать логику обертки с помощью классов или признаков, я пришел к следующему:

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
    }
  }
}

Теперь MyList может быть реализовано:

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
}

Это определенно лучше, чем иметь весь шаблонный код в определении MyList, но это еще много, чтобы написать MyList просто обертку для List.

...