3

Really struggling to figure out extending the immutable Set with a class that will represent a Set of concrete type. I'm doing this to try and create a nice DSL.

I'd like to have a class Thing, and when you add 'things' together you get a ThingSet object, which extends Set.

class Thing(val name:String){
  def +(other: Thing):ThingSet = new ThingSet() + other
}

I just can't figure out how to make the ThingSet object. I know I need to mix in traits like GenericSetTemplate, SetLike etc. But I just can't make it work.

Please, can anybody give me some pointers, as I can't find anything explicit enough to learn from. I've tried looking at the BitSet and HashSet implementations, but get lost.

Collin
  • 1,431
  • 12
  • 19
user523071
  • 281
  • 1
  • 3
  • 11

2 Answers2

9

Adapting from this Daily Scala post as well as the source to BitSet, which is a wonderful example here because it is not a parameterized collection, and is rather short and simple.

import scala.collection.SetLike
import scala.collection.generic.{GenericSetTemplate, GenericCompanion, CanBuildFrom}
import scala.collection.mutable.{Builder, SetBuilder}

class ThingSet(seq : Thing*) extends Set[Thing] 
                             with SetLike[Thing, ThingSet]
                             with Serializable {
    override def empty: ThingSet = new ThingSet()
    def + (elem: Thing) : ThingSet = if (seq contains elem) this 
        else new ThingSet(elem +: seq: _*)
    def - (elem: Thing) : ThingSet = if (!(seq contains elem)) this
        else new ThingSet(seq filterNot (elem ==): _*)
    def contains (elem: Thing) : Boolean = seq exists (elem ==)
    def iterator : Iterator[Thing] = seq.iterator
}

object ThingSet {
    def empty: ThingSet = new ThingSet()
    def newBuilder: Builder[Thing, ThingSet] = new SetBuilder[Thing, ThingSet](empty)
    def apply(elems: Thing*): ThingSet = (empty /: elems) (_ + _)
    def thingSetCanBuildFrom = new CanBuildFrom[ThingSet, Thing, ThingSet] {
        def apply(from: ThingSet) = newBuilder
        def apply() = newBuilder
    }
}
John in MD
  • 2,141
  • 5
  • 25
  • 36
Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681
3

From the scaladocs on Set, I think you just have to implement 4 or 5 methods. In this case I've decided to use a backing Seq[Thing] to create my actual set implementation.

class ThingSet(things: Seq[Thing]) extends Set[Thing] {
  def contains(key: Thing) = { things.contains(key) }
  def iterator: Iterator[Thing] = { things.iterator }
  def +(elem: Thing) = new ThingSet(things :+ elem)
  def -(elem: Thing) = new ThingSet(things.filterNot(_ == elem))
  override def empty = new ThingSet(Nil)
}

class Thing(val name: String) {
  def +(other: Thing) = { new ThingSet(List(this,other)) }
  override def toString = name
}

val thing = new Thing("I'm a new thing")
println(thing + new Thing("I'm some other thing"))

results in:

Set(I'm a new thing, I'm some other thing)

note: I think you have to run this in a script file rather than the REPL because of the cyclical dependency between Thing and ThingSet.

Collin
  • 1,431
  • 12
  • 19
  • The Set methods I implemented do with the exception of the first two. What manipulation are you referencing? – Collin Dec 14 '10 at 00:50
  • Methods like `map` or `filter`. – Daniel C. Sobral Dec 14 '10 at 01:14
  • This is wrong implementation and I accidentally +1ed it. It doesn't obey the rules of set. Produces duplicate elements unlike original set new ThingSet(Seq(new Thing("dog"), new Thing("cat"), new Thing("dog"))) res1: ThingSet = Set(dog, cat, dog) Set("dog","cat","dog") res2: scala.collection.immutable.Set[String] = Set(dog, cat) – Ravi Jul 07 '17 at 01:28