2

In Scala, can I limit a generic parameter to be a subset of a super type ?

sealed trait A
class X extends A
class Y extends A
class Z extends A

def myStrictFunc[T is X or Y, but not Z](o: T)
                 ----------+----------
                           |
                           Can I have this constraint in Scala ?
kuang
  • 687
  • 1
  • 6
  • 17

3 Answers3

1

Yes, you can. For example using Shapeless

import shapeless.=:!=

sealed trait A
class X extends A
class Y extends A
class Z extends A

def myStrictFunc[T <: A](o: T)(implicit ev: T =:!= Z) = ???

Otherwise you can implement =:!= manually:

Enforce type difference

Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
1

Dmytro Mitin's answer is a good one if you need to exclude one out of many. If, on the other hand, you need to restrict inclusion to a few, out of the many, here's one way to do it.

sealed trait A {val id: Int}
class W extends A {val id = 13}
class X extends A {val id = 17}
class Y extends A {val id = 18}
class Z extends A {val id = 21}

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

def myStrictFunc[T <: A : Union[X,Y]#Check](t: T): Int = t.id

myStrictFunc(new X)  //res0: Int = 17
myStrictFunc(new Y)  //res1: Int = 18
myStrictFunc(new Z)  //won't compile
myStrictFunc(new W)  //won't compile

The Union type can be expanded to 3, 4, or more types, but the code gets a little verbose.

jwvh
  • 50,871
  • 7
  • 38
  • 64
  • Just for completeness, to exclude a few out of many one can use `<:!<` https://github.com/milessabin/shapeless/blob/master/core/src/main/scala/shapeless/package.scala#L47-L52 – Dmytro Mitin Apr 09 '18 at 09:16
1

Using Shapeless is probably the simplest way to do that right now, however, you might want to check Dotty, which is the next generation Scala compiler and has built-in Union types.