I'm trying to extend a SortedMap in Scala, but I'm having some problems with SortedMapLike and canBuildFrom (the last one I can't even type it correctly). Here's some code; first the companion object:
object Timeline {
...
def newBuilder[A]: Builder[(Long, A), Timeline[A]] =
new ListBuffer[(Long, A)] mapResult fromSeq
def fromSeq[A](buf: Seq[(Long, A)]): Timeline[A] =
new Timeline(buf toMap)
def empty[A] = Timeline[A](Map[Long, A]())
}
Then the class (yes, all my Timelines are from Long to A):
final class Timeline[A] private(t: Map[Long, A])
extends SortedMap[Long, A]
with SortedMapLike[Long, A, Timeline[A]] {
private[this] lazy val iMap =
TreeMap(t.toArray: _*)(Ordering.fromLessThan[Long](_ > _))
override def newBuilder: Builder[(Long, A), Timeline[A]] = Timeline.newBuilder
override def empty: Timeline[A] = Timeline.empty
def -(key: Long) = Timeline(iMap - key)
def get(key: Long) = iMap.get(key)
def rangeImpl(from: Option[Long], until: Option[Long]) =
Timeline(iMap.rangeImpl(from, until))
def iterator = iMap.iterator
def ordering = iMap.ordering
}
I'm not sure all the above is the correct way to achieve this, but now comes the part I can't even type correctly:
implicit def canBuildFrom[A]: CanBuildFrom[Timeline[A], A, Timeline[A]] =
new CanBuildFrom[Timeline[A], A, Timeline[A]] {
def apply(): Builder[(Long, A), Timeline[A]] = newBuilder[A]
def apply(from: Timeline[A]): Builder[(Long, A), Timeline[A]] = newBuilder[A]
}
It seems I was incorrectly typing canBuildFrom:
implicit def canBuildFrom[A]: CanBuildFrom[Timeline[A], (Long, A), Timeline[A]] =
new CanBuildFrom[Timeline[A], (Long, A), Timeline[A]] {
def apply(): Builder[(Long, A), Timeline[A]] = newBuilder[A]
def apply(from: Timeline[A]): Builder[(Long, A), Timeline[A]] = newBuilder[A]
}
Related
Edit:
Last revision was deemed unhelpful as it did not contain necessary information that help narrow down my issue. hence the need to also include the AST.
Below is a library in its entirety that allows parsing and writing of play-json's json based on user defined schema; Similar to what Scala's slick offers for database columns to some extent:
import scala.language.higherKinds
import play.api.libs.functional.syntax._
import play.api.libs.json._
import scala.language.{higherKinds, implicitConversions}
type PathNodes = List[PathNode]
sealed trait Field[A] {
def pathNodes: PathNodes
def jsPath: JsPath = JsPath(pathNodes)
def relativePath: JsPath = JsPath(List(pathNodes.last))
def format: Format[A]
def nestedFormatter(path: JsPath): OFormat[A]
def nestedFormat: OFormat[A] = nestedFormatter(relativePath)
}
case class PlainField[A: Format](prefix: PathNodes) extends Field[A] {
override def pathNodes: PathNodes = prefix
def format: Format[A] = implicitly[Format[A]]
override def nestedFormatter(path: JsPath): OFormat[A] = path.format(format)
}
abstract class JsonSchema[T](val _prefix: PathNodes) extends Field[T] with SchemaExtensionMethods {
override def pathNodes: PathNodes = _prefix
def format: OFormat[T]
protected def plain[A: Format](name: String): PlainField[A] = PlainField[A](_prefix :+ KeyPathNode(name))
protected def nested[N](name: String, factory: PathNodes => N): N = factory(_prefix :+ KeyPathNode(name))
protected def nested[B, G <: JsonSchema[B]](name: String)(implicit sm: HasJsonSchema[B, G]): G = sm.apply(_prefix :+ KeyPathNode(name))
override def nestedFormatter(path: JsPath): OFormat[T] = path.format(format)
}
case class Optional[F, A](field: F)(implicit ev: F <:< Field[A]) extends Field[Option[A]] {
override def pathNodes: PathNodes = field.pathNodes
override def format: Format[Option[A]] = {
implicit val writes: Writes[Option[A]] = JsPath.writeNullable(field.format)
implicit val reads: Reads[Option[A]] = JsPath.readNullable(field.format)
implicitly[Format[Option[A]]]
}
def map[G, B](f: F => G)(implicit ev: G <:< Field[B]): Optional[G, B] = new Optional[G, B](f(field))
def flatMap[G <: Field[B], B](f: F => Optional[G, B]): Optional[G, B] = f(field)
override def nestedFormatter(path: JsPath): OFormat[Option[A]] = path.formatNullable(field.format)
}
case class Collection[F, A](field: F)(implicit ev: F <:< Field[A], repath: Repath[F]) extends Field[Seq[A]] {
override def pathNodes: PathNodes = field.pathNodes
override def format: Format[Seq[A]] = {
implicit val writes: Writes[Seq[A]] = Writes.seq(field.format)
implicit val reads: Reads[Seq[A]] = Reads.seq(field.format)
implicitly[Format[Seq[A]]]
}
def apply(idx: Int): F = implicitly[Repath[F]].apply(field, IdxPathNode(idx))
override def nestedFormatter(path: JsPath): OFormat[Seq[A]] = path.format(format)
}
class FormatExtensionMethods[T](val arg: T) {
def <>[A, B, Fun](apply: Fun, unapply: B => Option[A])(implicit jss: JsonShape[A, B, T, Fun]): OFormat[B] = jss.format(arg, apply, unapply andThen (_.get))
}
class FieldExtensionMethods[F](val field: F) {
def optional[A](implicit ev: F <:< Field[A]): Optional[F, A] = new Optional[F, A](field)
def sequence[A](implicit ev: F <:< Field[A], repath: Repath[F]): Collection[F, A] = new Collection[F, A](field)
}
trait SchemaExtensionMethods {
implicit def formatExtensionMethods[M](t: M): FormatExtensionMethods[M] = new FormatExtensionMethods[M](t)
implicit def fieldExtensionMethods[M, A](t: M): FieldExtensionMethods[M] = new FieldExtensionMethods[M](t)
}
trait Repath[F] {
def apply(f: F, node: PathNode): F
}
object Repath {
implicit def plain[T]: Repath[PlainField[T]] = new Repath[PlainField[T]] {
override def apply(t: PlainField[T], node: PathNode): PlainField[T] =
PlainField[T](t.pathNodes :+ node)(t.format)
}
implicit def schema[S <: JsonSchema[_]](implicit sm: HasJsonSchema[_, S]): Repath[S] = new Repath[S] {
override def apply(t: S, node: PathNode): S =
sm.apply(t.pathNodes :+ node)
}
implicit def option[F <: Field[T] : Repath, T]: Repath[Optional[F, T]] = new Repath[Optional[F, T]] {
override def apply(t: Optional[F, T], node: PathNode): Optional[F, T] =
new Optional[F, T](implicitly[Repath[F]].apply(t.field, node))
}
implicit def sequence[F <: Field[T] : Repath, T]: Repath[Collection[F, T]] = new Repath[Collection[F, T]] {
override def apply(t: Collection[F, T], node: PathNode): Collection[F, T] =
new Collection[F, T](implicitly[Repath[F]].apply(t.field, node))
}
}
trait JsonShape[A, B, -T, Func] {
def format(t: T, apply: Func, unapply: B => A): OFormat[B]
}
object JsonShape {
type F[T] = Field[T]
implicit def cc1[A, B]: JsonShape[A, B, F[A], (A) => B] = (t: F[A], apply: (A) => B, unapply: B => A) => {
val name = t.pathNodes.last.asInstanceOf[KeyPathNode].key
OFormat[B](
Reads[B](jsv => (jsv \ name).validate[A](t.format).map(apply)),
OWrites[B](b => JsObject(Map(name -> Json.toJson(unapply(b))(t.format))))
)
}
implicit def cc2[T1, T2, B]: JsonShape[(T1, T2), B, (F[T1], F[T2]), (T1, T2) => B] = (t: (F[T1], F[T2]), apply: (T1, T2) => B, unapply: B => (T1, T2)) => {
(
t._1.nestedFormat and
t._2.nestedFormat
) (apply, unapply)
}
implicit def cc3[T1, T2, T3, B]: JsonShape[(T1, T2, T3), B, (F[T1], F[T2], F[T3]), (T1, T2, T3) => B] = (t: (F[T1], F[T2], F[T3]), apply: (T1, T2, T3) => B, unapply: B => (T1, T2, T3)) => {
(
t._1.nestedFormat and
t._2.nestedFormat and
t._3.nestedFormat
) (apply, unapply)
}
//this goes up to 22
}
abstract class HasJsonSchema[T, +S <: JsonSchema[T]](val apply: PathNodes => S) extends OFormat[T] {
val root: S = apply(Nil)
def format: OFormat[T] = root.format
def writes(o: T): JsObject = root.format.writes(o)
def reads(json: JsValue): JsResult[T] = root.format.reads(json)
}
Now let's write a small piece of client code that reproduce the issue:
case class MessageSchema(prefix: PathNodes) extends JsonSchema[Message](prefix) {
def underlying = plain[String]("underlying")
//def underlying = plain[String]("underlying").optional if I wanted the field to be Option[String]
//def underlying = plain[String]("underlying").sequence if I wanted the field to be Seq[String]
override def format = underlying <> (Message.apply _, Message.unapply)
}
case class Message(underlying: String)
object Message {
implicit object sm extends HasJsonSchema[Message, MessageSchema](MessageSchema.apply)
}
case class LanguageTaggedSchema[T, S <: JsonSchema[T]](prefix: PathNodes)(implicit evT: HasJsonSchema[T, S]) extends JsonSchema[LanguageTagged[T]](prefix) {
def lang = plain[String]("lang")
def data: S = nested("data")(evT)
def format = (lang, data) <> (LanguageTagged.apply[T] _, LanguageTagged.unapply[T])
}
case class LanguageTagged[T](lang: String, data: T)
object LanguageTagged {
implicit def schemaMapper[T, S <: JsonSchema[T]](implicit ev: HasJsonSchema[T, S]): HasJsonSchema[LanguageTagged[T], LanguageTaggedSchema[T, S]] =
new HasJsonSchema[LanguageTagged[T], LanguageTaggedSchema[T, S]](LanguageTaggedSchema.apply[T, S]) {}
}
def toJson[T, S <: JsonSchema[T]](a: T)(implicit ev: HasJsonSchema[T, S]): JsValue = Json.toJson(a)(ev.format)
toJson(Message("hi")) //Ok!
toJson(LanguageTagged("en", Message("hi"))) //Ok!
//or simply write
Json.toJson(LanguageTagged("en", Message("hi")))
//and if i wanted to traverse a json path i would do:
val schema = implicitly[HasJsonSchema[LanguageTagged[Message],LanguageTaggedSchema[Message,MessageSchema]]].root
schema.data.underlying.jsPath
//prints: res2: play.api.libs.json.JsPath = /data/underlying
//Now to where the problem starts:
def getSchema[T, S <: JsonSchema[T]](a: T)(implicit ev: HasJsonSchema[T, S]): S = ev.root
getSchema(Message("hi")) //Ok!
getSchema(LanguageTagged("en", Message("hi"))) //Not Ok but why?
//Error:(211, 11) could not find implicit value for
//parameter ev: A$A6.this.HasJsonSchema[A$A6.this.LanguageTagged[A$A6.this.Message],S]
//getSchema(LanguageTagged("en", Message("hi")));//
//^
I have a huge suspicion that the compiler runs into issues because of the bounded type of S inHasJsonSchema[T, S <: JsonSchema[T]] when infering the implicit type S. and so far only in that specific situation as shown on the last line of all the code. as a dubugging attempt I created a similar situation and realized that if the type S was not bounded I wouldn't have this issue. Any sort of solution that refactors the code such that it doesn't depend on bounded types or one that simply solves the implicit resolution is appreciated
What You're trying to achieve cannot be done with subtyping. You should use type-classes instead, a more in-depth explanation:
http://danielwestheide.com/blog/2013/02/06/the-neophytes-guide-to-scala-part-12-type-classes.html
do you think what i did make sense? Is there a better way do encode a case class into Item? e.g. i'm not happy with ignoring in some cases an input param!
import shapeless.labelled.FieldType
import shapeless.{::, DepFn2, HList, HNil, LabelledGeneric, Witness}
import scala.collection.mutable
// mock of sdk item
class Item(val map: mutable.Map[String, Any] = mutable.Map[String, Any]()) {
def getString(attrName: String): String = map.get(attrName).get.asInstanceOf[String]
def getInt(attrName: String): Int = map.get(attrName).get.asInstanceOf[Int]
def getBoolean(attrName: String): Boolean = map.get(attrName).get.asInstanceOf[Boolean]
// def getMap(attrName: String): Map[String, String] = Map("attrName" -> "attrValue")
def setString(attrName: String, value: String): Unit = map.put(attrName, value)
def setInt(attrName: String, value: Int): Unit = map.put(attrName, value)
def setBoolean(attrName: String, value: Boolean): Unit = map.put(attrName, value)
override def toString() = map.toString()
}
trait ItemEncoder[A] extends DepFn2[String, A] {
type Out = Item
}
object ItemEncoder {
def apply[A](implicit encoder: ItemEncoder[A]): ItemEncoder[A] = encoder
def instance[A](f: (String, A) => Item): ItemEncoder[A] =
new ItemEncoder[A] {
override def apply(attrName: String, value: A): Out = f(attrName, value)
}
}
implicit val stringEncoder: ItemEncoder[String] =
ItemEncoder.instance { (attrName, value) =>
val item = new Item()
item.setString(attrName, value)
item
}
implicit val intEncoder: ItemEncoder[Int] =
ItemEncoder.instance { (attrName, value) =>
val item = new Item()
item.setInt(attrName, value)
item
}
implicit val booleanEncoder: ItemEncoder[Boolean] =
ItemEncoder.instance { (attrName, value) =>
val item = new Item()
item.setBoolean(attrName, value)
item
}
implicit val hnilEncoder: ItemEncoder[HNil] =
ItemEncoder.instance((attrName, value) => new Item())
def merge(i1: Item, i2: Item): Item = new Item(i1.map ++ i2.map)
implicit def hlistEncoder[K <: Symbol, L, H, T <: HList](
implicit
witness: Witness.Aux[K],
hEncoder: ItemEncoder[H],
tEncoder: ItemEncoder[T]
): ItemEncoder[FieldType[K, H] :: T] = {
ItemEncoder.instance { (_, value) =>
val attrName = witness.value.name
merge(hEncoder.apply(attrName, value.head), tEncoder.apply(attrName, value.tail))
}
}
implicit def genericEncoder[A, R](
implicit
generic: LabelledGeneric.Aux[A, R],
itemEncoder: ItemEncoder[R]
): ItemEncoder[A] =
ItemEncoder.instance { (attrName, value) =>
itemEncoder.apply(attrName, generic.to(value))
}
case class Person(name: String, age: Int, married: Boolean, manager: Boolean)
case class IceCream(name: String, subName: String, price: Int)
val genericPerson = LabelledGeneric[Person].to(Person("bob", 37, true, true))
def encode[A](toEncode: A)(implicit itemEncoder: ItemEncoder[A]) =
itemEncoder("", toEncode)
Maybe it will be better to use ToMap or something like this, and the convert it to Item
After getting deeper into the topic i managed to implement ItemEncoder that converts a case class with arbitrary nesting into Item like this:
import com.amazonaws.services.dynamodbv2.document.Item
import shapeless.labelled.FieldType
import shapeless.{::, HList, HNil, LabelledGeneric, Witness, _}
trait ItemEncoder[A] {
def encode(value: A): Item
}
object ItemEncoder {
def apply[A](implicit encoder: ItemEncoder[A]): ItemEncoder[A] = encoder
def instance[A](f: A => Item): ItemEncoder[A] =
new ItemEncoder[A] {
override def encode(value: A): Item = f(value)
}
implicit def stringEncoder[K <: Symbol, V <: String](
implicit witness: Witness.Aux[K]
): ItemEncoder[FieldType[K, V]] =
instance { value =>
val item = new Item
item.withString(witness.value.name, value)
item
}
implicit def intEncoder[K <: Symbol, V <: Int](
implicit witness: Witness.Aux[K]
): ItemEncoder[FieldType[K, V]] =
instance { value =>
val item = new Item
item.withInt(witness.value.name, value)
item
}
implicit def booleanEncoder[K <: Symbol, V <: Boolean](
implicit witness: Witness.Aux[K]
): ItemEncoder[FieldType[K, V]] =
instance { value =>
val item = new Item
item.withBoolean(witness.value.name, value)
item
}
// K is key, A is value, R is HList representation of A
implicit def nestedClassEncoder[K <: Symbol, A, R](
implicit
witness: Witness.Aux[K],
generic: LabelledGeneric.Aux[A, R],
encoder: ItemEncoder[R]
): ItemEncoder[FieldType[K, A]] =
instance { value =>
val i = encoder.encode(generic.to(value))
val item = new Item
val m = new java.util.HashMap[String, Any]()
item.withMap(witness.value.name, i.asMap())
item
}
import cats.Monoid
implicit val itemMonoid: Monoid[Item] = new Monoid[Item] {
override def empty: Item = new Item()
override def combine(x: Item, y: Item): Item = {
val m = x.asMap
m.putAll(y.asMap())
Item.fromMap(m)
}
}
implicit val hnilEncoder: ItemEncoder[HNil] =
instance(_ => new Item())
implicit def hlistEncoder[H, T <: HList](
implicit
hEncoder: Lazy[ItemEncoder[H]],
tEncoder: ItemEncoder[T],
monoid: Monoid[Item]
): ItemEncoder[H :: T] =
instance { value =>
// println("hlist enc")
val itemX = hEncoder.value.encode(value.head)
val itemY = tEncoder.encode(value.tail)
monoid.combine(itemX, itemY)
}
implicit def genericEncoder[A, R](
implicit
generic: LabelledGeneric.Aux[A, R],
itemEncoder: Lazy[ItemEncoder[R]]
): ItemEncoder[A] =
instance { value =>
// println("gen enc")
itemEncoder.value.encode(generic.to(value))
}
def encode[A](toEncode: A)(implicit itemEncoder: ItemEncoder[A]) =
itemEncoder.encode(toEncode)
}
Current implementation is a bit simplified. So it contains ItemEncoder implementation only for such a primitive types as String, Int and Boolean. But other primitive types can be easily added by using the present ones as example.
You can find complete implementation with QuickCheck tests on Git
I've got an interface,
trait Heap[E, H <: Heap[E, H]] {
implicit def ord: Ordering[E]
def empty: H
def isEmpty: Boolean
def insert(x: E): H
def merge(b: H): H
def findMin: E
def deleteMin: H
def toList: List[E] =
if (isEmpty) Nil else findMin :: deleteMin.toList
}
It's generic, as we need H to define "a Heap of the same type," as def merge(b: H): H is normally defined in terms of merging heaps of the same internal structure.
That works reasonably well for the "normal" heaps, i.e. heaps that manage their internal structures themselves:
class LazyPairingHeap[E](val h: Repr[E] = Empty)
(implicit val ord: Ordering[E])
extends Heap[E, LazyPairingHeap[E]] {
import okasaki.heaps.LazyPairingHeap._
override def empty = new LazyPairingHeap[E](Empty)
override def isEmpty: Boolean = h == Empty
// etc.
and even for some of the heaps build on top of other heaps:
class SizedHeap[E, H <: Heap[E, H]](val s: Int, val h: H)
extends Heap[E, SizedHeap[E, H]] {
override implicit def ord: Ordering[E] = h.ord
override def empty = new SizedHeap[E, H](0, h.empty)
override def isEmpty = s == 0
override def insert(e: E) = new SizedHeap[E, H](s + 1, h.insert(e))
override def merge(o: SizedHeap[E, H]) = new SizedHeap[E, H](s + o.s, h.merge(o.h))
override def findMin: E = h.findMin
override def deleteMin = new SizedHeap[E, H](s - 1, h.deleteMin)
}
The problem appears when instead of merely wrapping the original heap we need to weave it into our representation:
object BootstrappedHeap {
sealed trait BSHeap[+A, +H[_]]
object Empty extends BSHeap[Nothing, Nothing] {
override def toString = "Empty"
}
case class H[A, BH[_]](x: A, bsh: BH[BSHeap[A, BH]]) extends BSHeap[A, BH] {
override def toString = s"H($x, $bsh)"
}
}
abstract class BootstrappedHeap[E,
BaseHeap[X] <: Heap[X, BaseHeap[X]],
This <: BootstrappedHeap[E, BaseHeap, This]]
(val h: BSHeap[E, BaseHeap] = Empty)
(implicit ord: Ordering[E])
extends Heap[E, This] {
implicit val hord: Ordering[BSHeap[E, BaseHeap]] =
Ordering.by {
case Empty => None
case H(x, _) => Some(x)
}
def baseHeap: BaseHeap[BSHeap[E, BaseHeap]]
def create(h: BSHeap[E, BaseHeap]): This
override def empty = create(Empty)
override def isEmpty: Boolean = h == Empty
override def insert(x: E) = create(merge(H(x, baseHeap.empty), h))
override def merge(o: This) = create(merge(h, o.h))
private def merge(a: BSHeap[E, BaseHeap], b: BSHeap[E, BaseHeap]): BSHeap[E, BaseHeap] = (a, b) match {
case (Empty, _) => b
case (_, Empty) => a
case (h1#H(x, p1), h2#H(y, p2)) =>
if (ord.lteq(x, y)) H(x, p1.insert(h2))
else H(y, p2.insert(h1))
}
// etc
Now the question is, is there a better way than this lovely type annotation?
[E,
BaseHeap[X] <: Heap[X, BaseHeap[X]],
This <: BootstrappedHeap[E, BaseHeap, This]]
X seems to be unnecessary though I found no way to get rid of it;
a reference to This seems to be a bit over the edge;
finally, BaseHeap has really nothing to do in the public interface: I want it to be an implementation detail, can I hide it?
I have something called a Generator:
trait Generator[A, B] {
def generate(in: Seq[A]): Seq[B]
}
I can provide a Bind instance for this generator:
object Generator {
implicit def generatorBind[T]: Bind[({type l[B] = Generator[T, B]})#l] = new Bind[({type l[B] = Generator[T, B]})#l] {
def map[A, B](generator: Generator[T, A])(f: A => B): Generator[T, B] = new Generator[T, B] {
def generate(in: Seq[T]): Seq[B] = generator.generate(in).map(f)
}
def bind[A, B](generator: Generator[T, A])(f: A =>Generator[T, B]): Generator[T, B] = new Generator[T, B] {
def generate(in: Seq[T]): Seq[B] = generator.generate(in).flatMap(v => f(v).generate(in))
}
}
}
Unfortunately, type inference is completely lost if I try to use my generators as applicative instances:
val g1 = new Generator[Int, Int] { def generate(seq: Seq[Int]) = seq.map(_ + 1) }
val g2 = new Generator[Int, Int] { def generate(seq: Seq[Int]) = seq.map(_ + 10) }
// doesn't compile
// can make it compile with ugly type annotations
val g3 = ^(g1, g2)(_ / _)
My only workaround for now has been to add a specialised method to the Generator object:
def ^[T, A, B, C](g1: Generator[T, A], g2: Generator[T, B])(f: (A, B) => C) =
generatorBind[T].apply2(g1, g2)(f)
Then this compiles:
val g4 = Generator.^(g1, g2)(_ / _)
Is there a workaround for this problem? I suppose there is because using State[S, A] as a Monad poses the same kind of issue (but in Scalaz there seems to be a special treatment for State).
You can use ApplicativeBuilder if explicitly annotate g1 and g2 types, or change to abstract class Generator
// java.lang.Object with Generator[Int, Int] !!!
val badInference = new Generator[Int, Int] { def generate(seq: Seq[Int]) = seq.map(_ + 1) }
val g1: Generator[Int, Int] = new Generator[Int, Int] { def generate(seq: Seq[Int]) = seq.map(_ + 1) }
val g2: Generator[Int, Int] = new Generator[Int, Int] { def generate(seq: Seq[Int]) = seq.map(_ + 10) }
val g3 = (g1 |#| g2)(_ / _)
I think implicit macro's fundep materialization (aka functional dependency) is to help this.
trait Iso[T, U] {
def to(t: T) : U
def from(u: U) : T
}
case class Foo(i: Int, s: String, b: Boolean)
def conv[C](c: C)(implicit iso: Iso[C, L]): L = iso.from(c)
val tp = conv(Foo(23, "foo", true))
It requires macro paradise tho.
The trait TraversableLike[+A, +Repr] allows one to make a collection where some functions will return a Repr, while others continue to return the type parameter That on the function. Is there a way to define a CustomCollection[A] where functions like map, ++, and others will default That as Repr if not inferred otherwise?
Here is a code snippet that hopefully describes what I would like:
case class CustomCollection[A](list: List[A]) extends TraversableLike[A, CustomCollection[A]] {
protected[this] def newBuilder = new CustomCollectionBuilder[A]
def foreach[U](f: (A) => U) {list foreach f}
def seq = list
}
class CustomCollectionBuilder[A] extends mutable.Builder[A, CustomCollection[A]] {
private val list = new mutable.ListBuffer[A]()
def += (elem: A): this.type = {
list += elem
this
}
def clear() {list.clear()}
def result(): CustomCollection[A] = CustomCollection(list.result())
}
object CustomCollection extends App {
val customCollection = CustomCollection(List(1, 2, 3))
println(customCollection filter {x => x == 1}) // CustomCollection(1)
println(customCollection map {x => x + 1}) // non-empty iterator
}
I would like the last line to be CustomCollection(2, 3, 4).
You need to set up a companion object which provides the refined CanBuildFrom instance:
import collection.TraversableLike
import collection.generic.{CanBuildFrom, GenericCompanion, GenericTraversableTemplate,
TraversableFactory}
import collection.mutable.{Builder, ListBuffer}
object CustomCollection extends TraversableFactory[CustomCollection] {
def newBuilder[A] = new CustomCollectionBuilder[A]
implicit def canBuildFrom[A]: CanBuildFrom[Coll, A, CustomCollection[A]] =
new CanBuildFrom[Coll, A, CustomCollection[A]] {
def apply(): Builder[A, CustomCollection[A]] = new CustomCollectionBuilder()
def apply(from: Coll): Builder[A, CustomCollection[A]] = apply()
}
}
case class CustomCollection[A](list: List[A]) extends Traversable[A]
with TraversableLike[A, CustomCollection[A]]
with GenericTraversableTemplate[A, CustomCollection] {
override def companion: GenericCompanion[CustomCollection] = CustomCollection
def foreach[U](f: A => U) { list foreach f }
override def seq = list
}
class CustomCollectionBuilder[A] extends Builder[A, CustomCollection[A]] {
private val list = new ListBuffer[A]()
def += (elem: A): this.type = {
list += elem
this
}
def clear() {list.clear()}
def result(): CustomCollection[A] = CustomCollection(list.result())
}
val customCollection = CustomCollection(List(1, 2, 3))
val f = customCollection filter {x => x == 1} // CustomCollection[Int]
val m = customCollection map {x => x + 1} // CustomCollection[Int]