Implicitly add to collection

Viewed 108

I have a Set, and I want to implicitly add another value to the end of that Set when it is called.

For example, I need something like this to work:

implicit def addToSet(set: Set[Int]) = set + 4
val s = Set(1, 2, 3)
println(s) // Set(1, 2, 3, 4)

Use case:

I am building a website (using PlayFramework), and I have about 20-30 Sets of roles which correspond to different pieces of functionality. For each page, only certain roles can access certain features, so I have a bit of functionality each time I need to check role privileges (in pseudocode: if(role.canAccess(/*name of page*/))) which checks if the role that the user is logged in as is contained in the Set which corresponds to /*name of page*/.

I am now creating a super user which has access to every piece of functionality on every page, so that user's "role" needs to be in every Set. I don't want to have to do this by manually adding this role to every Set because I want this to be scalable and easily maintainable, so I was hoping for a solution which just adds this new super role to each Set automatically behind the scenes.

I'm open to non-implicit answers too, as long as they don't modify the original Sets I already have.

3 Answers

I think that's what you are looking for:

trait Implicits {
  implicit class AddIntoSet[T](set: Set[T]) {
    def add(t: T): Set[T] = {
      set.+(t)
    }
  }
}


object Solution1 extends App with Implicits {
  val s = Set(1, 2, 3)
  println(s.add(4)) // Set(1, 2, 3, 4)
}

Like others have said, it might not be a very good idea but if you really must, You could try rolling your own Set object with slight modifications:

import scala.collection.generic.SetFactory
import scala.collection.{GenSet, Set, immutable}

object CustomSet extends SetFactory[Set]{
  def newBuilder[A] = immutable.Set.newBuilder[A]

  def apply[T](elems: T*)(implicit extra: T): Set[T] = {
    val orig: Set[T] = super.apply(elems).flatten
    val withExtra: Set[T] = orig union GenSet(extra)
    withExtra
  }
}

implicit val extraInt: Int = 4
implicit val extraStr: String = "c"

val myNumNewSet: Set[Int] = CustomSet(1,2,3)
val myStrNewSet: Set[String] = CustomSet("a", "b")

And these will return:

scala> val myNumNewSet: Set[Int] = CustomSet(1,2,3)
myNumNewSet: scala.collection.Set[Int] = Set(1, 2, 3, 4)

scala> val myStrNewSet: Set[String] = CustomSet("a", "b")
myStrNewSet: scala.collection.Set[String] = Set(a, b, c)

Maybe using type alias would be acceptable for you?

object Roles {
  type Role = Int //Optionally you can use type tagging to make it more type-safe
  type Roles = Set[Role]

  val SuperRole = 4

  def apply(roles: Role*): Roles = {
    Set(SuperRole) ++ roles
  }
}



println(Roles(1,2,3)) // Set(4, 1, 2, 3)
println(Roles() ++ Set(5,6)) // you can also use all methods from Set

That has also advantage, that if you use it in any other scope than roles it wouldn't interfere:

val idsOfUsers = Set(10,12,22) // Set(10, 12, 22)
val roles = Roles(10,12,22) //Set(4, 10, 12, 22)
Related