I am learning from this code snippest https://gist.github.com/tiqwab/bc8d372ca489a74b72dd2357e7d6b010 to create a custom scala Seq class.
package com.tiqwab.example.step4
import scala.collection.mutable.ListBuffer
import scala.collection.{immutable, mutable, SeqFactory, StrictOptimizedLinearSeqOps}
class MyList[+A: ClassTag] private (elems: List[A])
extends immutable.LinearSeq[A] with immutable.LinearSeqOps[A, MyList, MyList[A]]
with StrictOptimizedLinearSeqOps[A, MyList, MyList[A]] {
// To be overridden in implementations, as said in scala.collection.LinearSeq
override def isEmpty: Boolean = elems.isEmpty
override def head: A = elems.head
override def tail: MyList[A] = new MyList(elems.tail)
override def iterator: Iterator[A] = elems.iterator
// To be overridden for LinearSeqOps
override def iterableFactory: SeqFactory[MyList] = MyList
override def toString: String = s"MyList(${elems.mkString(",")})"
}
object MyList extends SeqFactory[MyList] {
override def from[A](source: IterableOnce[A]): MyList[A] =
newBuilder[A].addAll(source).result()
override def empty[A]: MyList[A] =
new MyList(List.empty)
override def newBuilder[A]: mutable.Builder[A, MyList[A]] =
new ListBuffer[A].mapResult(elems => new MyList(elems))
}
The question is how can I attach a ClassTag to the type parameter A, if I literally attach the tag to that, I can't override the methods from SeqFactory anymore. Because the method signature is no longer consistent with the one from SeqFactory. Or it there a better way to implement such Custom collection with class tag?
package com.tiqwab.example.step4
import scala.collection.mutable.ListBuffer
import scala.collection.{immutable, mutable, SeqFactory, StrictOptimizedLinearSeqOps}
class MyList[+A] private (elems: List[A])
extends immutable.LinearSeq[A] with immutable.LinearSeqOps[A, MyList, MyList[A]]
with StrictOptimizedLinearSeqOps[A, MyList, MyList[A]] {
// To be overridden in implementations, as said in scala.collection.LinearSeq
override def isEmpty: Boolean = elems.isEmpty
override def head: A = elems.head
override def tail: MyList[A] = new MyList(elems.tail)
override def iterator: Iterator[A] = elems.iterator
// To be overridden for LinearSeqOps
override def iterableFactory: SeqFactory[MyList] = MyList
override def toString: String = s"MyList(${elems.mkString(",")})"
}
object MyList extends SeqFactory[MyList] {
override def from[A: ClassTag](source: IterableOnce[A]): MyList[A] =
newBuilder[A].addAll(source).result()
override def empty[A: ClassTag]: MyList[A] =
new MyList(List.empty)
override def newBuilder[A: ClassTag]: mutable.Builder[A, MyList[A]] =
new ListBuffer[A].mapResult(elems => new MyList(elems))
}
You'd have to modify the factory, so that it would NOT be an object
but a class with a constructor, and pass ClassTag
there:
class MyListFactory[A: ClassTag] {
override def from[A](source: IterableOnce[A]): MyList[A] =
newBuilder[A].addAll(source).result()
override def empty[A]: MyList[A] =
new MyList(List.empty)
override def newBuilder[A]: mutable.Builder[A, MyList[A]] =
new ListBuffer[A].mapResult(elems => new MyList(elems))
}
object MyList {
// one way of creating this things
def apply[A: ClassTag]: MyListFactory[A] = new MyListFactory[A]
// another way, requires enabling implicit conversions
implicit def convertToFactory[A: ClassTag](
notUsed: MyList.type // maybe add some @unused annotation here
): MyListFactory[A] = new MyListFactory[A]
}
Or, you can also take a look how stdlib implemented e.g. SortedSet
since it requires Ordering[A]
, although your code might benefit from using ClassTagIterableFactory
instead of SortedIterableFactory
.
object MyList extends ClassTagIterableFactory[MyList] {
override def from[A: ClassTag](source: IterableOnce[A]): MyList[A] =
newBuilder[A].addAll(source).result()
override def empty[A: ClassTag]: MyList[A] =
new MyList(List.empty)
override def newBuilder[A: ClassTag]: mutable.Builder[A, MyList[A]] =
new ListBuffer[A].mapResult(elems => new MyList(elems))
}
It should be more aligned with what you're trying to achieve (although I would avoid defining a custom collection if I didn't have a good reason to do so).