Miles Sabin describes a very nice way to get union type in his recent blog post Unboxed union types in Scala via the Curry-Howard isomorphism:
He first defines negation of types as
type ¬[A] = A => Nothing
using De Morgan's law this allows him to define union types
type ∨[T, U] = ¬[¬[T] with ¬[U]]
With the following auxiliary constructs
type ¬¬[A] = ¬[¬[A]]
type |∨|[T, U] = { type λ[X] = ¬¬[X] <:< (T ∨ U) }
you can write union types as follows:
def size[T : (Int |∨| String)#λ](t : T) = t match {
case i : Int => i
case s : String => s.length
}
Well, in the specific case of Any*
, this trick below won't work, as it will not accept mixed types. However, since mixed types wouldn't work with overloading either, this may be what you want.
First, declare a class with the types you wish to accept as below:
class StringOrInt[T]
object StringOrInt {
implicit object IntWitness extends StringOrInt[Int]
implicit object StringWitness extends StringOrInt[String]
}
Next, declare foo
like this:
object Bar {
def foo[T: StringOrInt](x: T) = x match {
case _: String => println("str")
case _: Int => println("int")
}
}
And that's it. You can call foo(5)
or foo("abc")
, and it will work, but try foo(true)
and it will fail. This could be side-stepped by the client code by creating a StringOrInt[Boolean]
, unless, as noted by Randall below, you make StringOrInt
a sealed
class.
It works because T: StringOrInt
means there's an implicit parameter of type StringOrInt[T]
, and because Scala looks inside companion objects of a type to see if there are implicits there to make code asking for that type work.
Dotty, a new experimental Scala compiler, supports union types (written A | B
), so you can do exactly what you wanted:
def foo(xs: (String | Int)*) = xs foreach {
case _: String => println("str")
case _: Int => println("int")
}
Here is the Rex Kerr way to encode union types. Straight and simple!
scala> def f[A](a: A)(implicit ev: (Int with String) <:< A) = a match {
| case i: Int => i + 1
| case s: String => s.length
| }
f: [A](a: A)(implicit ev: <:<[Int with String,A])Int
scala> f(3)
res0: Int = 4
scala> f("hello")
res1: Int = 5
scala> f(9.2)
<console>:9: error: Cannot prove that Int with String <:< Double.
f(9.2)
^
Source: Comment #27 under this excellent blog post by Miles Sabin which provides another way of encoding union types in Scala.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With