erdavila - 1 year ago 66
Scala Question

# Define common lower bounds of parameters

In the following function, the Scala compiler is able to define the return type to the lowest common supertype of the values used in the if/else expression:

``````def cond(b: Boolean, t: A, f: B) = if (b) t else f
``````

Considering the following hierarchy:

``````class X
class A extends X
class B extends X
``````

the function
`cond`
above is defined as returning a value of type
`X`
.

However, if
`A`
and
`B`
are type parameters in the definition of the
`cond`
function, its return type is
`Any`
:

``````def cond[A, B](b: Boolean, t: A, f: B) = if (b) t else f
``````

Is it possible to make the compiler use the lowest common supertype of the type parameters?

I tried some variations of the following, without success:

``````def cond[A, B, R >: A with B](b: Boolean, t: A, f: B): R = if (b) t else f
def cond[A, B, R](b: Boolean, t: A, f: B)(implicit ev1: A <:< R, ev2: B <:< R): R = if (b) t else f
``````

EDIT: The question above is oversimplified. In fact my real problem had one of the type parameters already resolved:

``````class C[A](b: Boolean, t: A) {
def cond[B](f: B) = if(b) t else f
}
``````

If you don't need the exacta types of the arguments, then the following will usually suffice:

``````def cond[T](b: Boolean, t: T, f: T) = if (b) t else f
``````

Scala will automatically upcast the types of the arguments to their least upper bound (LUB):

``````scala> cond(true, new A, new B)
res0: X = A@74a59bb6
``````

But if you need the exact types, for example for implicit resolution, I believe the following trick should work:

``````def cond[A, B, C >: A](b: Boolean, t: A, f: B with C): C = if (b) t else f
``````

Here `A` and `B` are exact types of the arguments, and `C` is their LUB. `C` has the constraint to be the supertype of `A`: `C >: A`, but it also should be the type of the second argument, because it's defined as `f: B with C`, and thus it is inferred as the LUB of `A` and `B`.

We can check correct type inference with this definition with the following code:

``````import reflect.runtime.universe._
def cond[A, B, C >: A](b: Boolean, t: A, f: B with C)(
implicit ta: TypeTag[A],
tb: TypeTag[B],
tc: TypeTag[C]
): C = {
println(ta)
println(tb)
println(tc)
if (b) t else f
}

scala> cond(true, new A, new B)
TypeTag[A]
TypeTag[B]
TypeTag[X]