scalasetiterablecustom-collection

Custom Collection extending on Set in Scala


This is a follow-up on the below question: How to create a custom collection that extends on Sets In Scala?

I have a basic class that extends on the Set collection and most of the functions like filter, collect and others are working :

import scala.collection._

class TestSet[A]
  extends mutable.Set[A]
    with mutable.SetOps[A, mutable.Set, TestSet[A]] {

  private var data = mutable.Set.empty[A]

  override protected def fromSpecific(coll: IterableOnce[A]): TestSet[A] = TestSet.from(coll)

  override protected def newSpecificBuilder: mutable.Builder[A, TestSet[A]] = TestSet.newBuilder

  override def empty: TestSet[A] = TestSet.empty

  override def clear(): Unit = {
    data = mutable.Set.empty
  }

  override def subtractOne(elem: A): TestSet.this.type = {
    data -= elem
    this
  }

  override def addOne(elem: A): TestSet.this.type = {
    data += elem
    this
  }

  override def contains(elem: A): Boolean = data.contains(elem)

  override def iterator: Iterator[A] = {
    data.iterator
  }

}

object TestSet {
  def empty[A] = new TestSet[A]


  def from[A](source: IterableOnce[A]): TestSet[A] =
    source match {
      case pm: TestSet[A] => pm
      case _ => (newBuilder ++= source).result()
    }

  def apply[A](elem: A*): TestSet[A] = from(elem)

  def newBuilder[A]: mutable.Builder[A, TestSet[A]] =
    new mutable.GrowableBuilder[A, TestSet[A]](empty)

  import scala.language.implicitConversions

  implicit def toFactory[A](self: this.type): Factory[A, TestSet[A]] =
    new Factory[A, TestSet[A]] {
      def fromSpecific(it: IterableOnce[A]): TestSet[A] = self.from(it)

      def newBuilder: mutable.Builder[A, TestSet[A]] = self.newBuilder
    }

}

It might not be possible but I originally wanted SetOps with the following types mutable.SetOps[A, TestSet, TestSet[A]] instead of mutable.SetOps[A, mutable.Set, TestSet[A]], but I am having trouble redefining iterableFactory.

A working snippet can be found here: https://scastie.scala-lang.org/4Qi8ZueDTpScDwxDfqptew


Solution

  • The scaladoc of IterableFactory says "Base trait for companion objects..." which is a hint that the companion of your TestSet should implement that trait.

    All you need to do is add...

    object TestSet extends IterableFactory[TestSet] {
       // ...
    }
    

    ...and then remove your def apply[A](elem: A*): TestSet[A] from the companion (because that is already inherited from IterableFactory).

    Then you'd do the following in your class:

    class TestSet[A]
      extends mutable.Set[A]
        with mutable.SetOps[A, TestSet, TestSet[A]] {
    
      // ...
    
      // returns your companion object
      override def iterableFactory: IterableFactory[TestSet] = TestSet
    
      // ...
    
    }
    
    

    And that's all it would need.

    (This is so simple because you already implemented all abstract members of IterableFactory[TestSet] in your companion object - it would not work if the companion were empty.)