Constraint Argument to Multiple Types

A lot of times while writing quick scripts (where I haven’t thought through well), I wish the compiler would be a bit lenient to allow any argument of different types. Like this:

class Animal
def getInt(n: Any) : Int = n match {
    case x: Int => x
    case x: String => Integer.parseInt(x)
    case x: Animal => 42
}

Duck typing looks well. But its too lenient for me. I dont like the ability to do getInt(new Object). I wish I was allowed to do:

def getInt(n: Int || String || Animal) : Int = n match {
    case x: Int => ....

One very smart way of doing it can be:

sealed trait GetIntType[-T]
object GetIntType{
implicit object IntType extends GetIntType[Int]
implicit object StringType extends GetIntType[String]
implicit object AnimalType extends GetIntType[Animal]
}

class Dog extends Animal

def getInt[T : GetIntType](n: T) : Int = n match {
case x: Int => x
case x: String => Integer.parseInt(x)
case x: Animal => 42
}

scala> getInt(1)
res7: Int = 1

scala> getInt("1")
res8: Int = 1

It works because, the compiler constraint is from the availability of implicit objects in `GetIntType` companion object. The context bound implicit def f: GetIntType[T] by default searches for implicit objects inside companion object of `GetIntType`. So now not only you can do this:

scala> getInt(new Animal)
res5: Int = 42</pre>

But also this:

scala> getInt(new Dog)
res6: Int = 42

It works because `GetIntType` is contra-variant. Which means if Dog <: Animal then GetIntType[Animal] <: GetIntType[Dog].

But I still find the above tedious. And too much baggage code.