3

I would ensure that a method should only accept instances of A or B or C. And I don't want to modify the code of A, B and C.

case class A
case class B
case class C
def method(aOrbOrc: Any) = ???

// method("toto") should not compile
BananaBuisness
  • 339
  • 2
  • 18
Yann Moisan
  • 8,161
  • 8
  • 47
  • 91

2 Answers2

4

You could use Miles Sabin's idea for implementing union types (code below is taken from Rex Kerr's variant):

trait Contra[-A] {}
type Union[A,B,C] = {
  type Check[Z] = Contra[Contra[Z]] <:< Contra[Contra[A] with Contra[B] with Contra[C]]
}

then do:

def method[T: Union[A,B,C]#Check](t: T) = ???

for example:

def method[T: Union[Int,String,Boolean]#Check](t:T) = ???

method(1)     // OK
method("foo") // OK
method(true)  // OK
method(1.5)   // does not compile

Read more about it here. And here is a link to Miles' post.

Community
  • 1
  • 1
Eduardo
  • 8,362
  • 6
  • 38
  • 72
  • This solution is really amazing. Unfortunately, I wasn't able to make it work for a `Map[String, Union[String,Int,List[String]]]`. It compiles but it doesn't really stop me from adding something like `(String, List[Int])`. Even though it works for methods it doesn't work for variable declarations. Any suggestion? – marios Jan 25 '16 at 01:52
4

You can use Type Class.

case class A(s: String)
case class B(i: Int)
case class C(i1: Int, i2: Int)

// Type Class
trait ABC[T] { 
    def bar(t: T): String
}
// Instances
implicit object ABC_A extends ABC[A] {
    override def bar(t: A): String = "A : s = " + t.s
}
implicit object ABC_B extends ABC[B] {
    override def bar(t: B): String = "B : i = " + t.i
}
implicit object ABC_C extends ABC[C] {
    override def bar(t: C): String = "C : i1 = " + t.i1 + ", i2 = " + t.i2
}

def method[T](abc: T)(implicit instance: ABC[T]) = println(instance.bar(abc))

method(A("AAAAA")) // => A : s = AAAAA
method(B(123))     // => B : i = 123
method(C(9, 5))    // => C : i1 = 9, i2 = 5
method(1)          // compilation error
ixguza
  • 413
  • 2
  • 8