4
votes

Bounds in Scala allow more granular control of types in Scala, for example argument types. For example

def foo[ S <: String ]( arg: S ): S = ...

The above allows a function to accept arguments that are sub types of String, also

def bar[ S >: A <: B ]( arg: S ): S = ...

The above allows the setting of an upper and lower bound so that S is a sub type of B and a super type of A.

My question is (I assume the bounds are inclusive) would it be possible to set up an argument type so that the argument would be a supertype of say String, but not include some super type of the lower bound (in this case String) say type Any.

UPDATE

sealed trait Helper[S]
object Helper {
  implicit def stringSubtype[S >: String] = new Helper[S]{}
  implicit def any = new Helper[Any]{}
}

def foo[S: Helper](r: S): Int = 1

val arg1: String = "hi"
val arg2: Any    = "hello"

foo(arg1)
foo(arg2)

I would expect that the call with arg2 should result in an illegal argument exception.

1

1 Answers

3
votes

For more complex type constraints like this you can do type-level programming via implicits:

sealed trait Helper[S]
object Helper {
  implicit def stringSubtype[S >: String] = new Helper[S]{}
  implicit def any = new Helper[Any]{}
  implicit def any2 = new Helper[Any]{}
}

def foo[S: Helper] = ...

foo can only be invoked with a type S for which an implicit Helper[S] can be resolved. stringSubtype supplies a Helper[S] for any S >: String. But for Any, both that and any apply and the two implicits conflict, so it's not possible (well, except by explicitly passing one or the other) to call foo[Any].

Update: it seems that any is somehow higher priority than stringSubtype, so it's resolved for foo[Any]. Rather than figure out exactly why this is, the simplest workaround was to also define any2.