Search code examples
scalasetiterablecustom-collection

How to create a custom collection that extends on Sets In Scala?


I want to create a new custom Scala collection from existing Set Collection which I can later extend with some additional functions.

For the moment, I am trying to make it behave as the standard collection Set. I tried the following link : https://docs.scala-lang.org/overviews/core/custom-collections.html where they have a custom collection that extends on Map. However extending on Set seems a bit different and I am having a ton of type incompatibility that I am not able to resolve.

Here's my starting code : the parts I am not able to define are addOne, substractOne, contains and iterator.

import scala.collection._

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

  override def empty: TestSet[A] = new TestSet

  // Members declared in scala.collection.mutable.Clearable
  override def clear(): Unit = immutable.Set.empty

  // Members declared in scala.collection.IterableOps
  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 className = "TestSet"

  //override def subtractOne(elem: A): TestSet.this.type = ???

  //override def addOne(elem: A): TestSet.this.type = ???

  //override def contains(elem: A): Boolean = ???

   //override def iterator: Iterator[A] = {
   //}
}

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
    }


}

Solution

  • I will interpret your question as "I want to do something similar as the Map-example from https://docs.scala-lang.org/overviews/core/custom-collections.html for a mutable Set but now am stuck with this code" and will try to answer that (ignoring any other aspects of your question).

    What you need to understand is that mutable.Set and mutable.SetOps are just traits that provide some reusable parts of implementation, but they do not contain any actual data structures.

    So if you want to implement your own implementation, you will have to provide the actual underlying data structure yourself (similar to how the PrefixMap from that link has private var suffixes and private var value).

    For example, you could use an underlying immutable Set like this:

    class TestSet[A]
      extends mutable.Set[A]
        with mutable.SetOps[A, mutable.Set, TestSet[A]] {
    
      // the underlying data structure
      private var data = mutable.Set.empty[A]
    
      // ATTENTION: your implementation was different and buggy
      override def clear(): Unit = {
        data = mutable.Set.empty
      }
    
      override def subtractOne(elem: A): TestSet.this.type = {
        data = data - elem
        this
      }
    
      override def addOne(elem: A): TestSet.this.type = {
        data = data + elem
        this
      }
    
      override def contains(elem: A): Boolean = data.contains(elem)
    
      override def iterator: Iterator[A] = {
        data.iterator
      }
      
      // ...
    }
    

    Note that the above is just an example of what you could do in order to get your code to work - I'm not saying that it's a good idea.