Reputation: 371
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
}
Upvotes: 0
Views: 60
Reputation: 371
Thanks to @nikhil and @Kolmar answers I could come up to this solution:
class C[A](b: Boolean, t: A) {
def cond[B <: R, R >: A](f: B): R = if(b) t else f
}
Upvotes: 0
Reputation: 19
You can apply upper type bounds to A and B,if you know LUB statically.
def cond[A <: C, B <: C,C](b: Boolean, t: A, f: B) = if (b) t else f
Upvotes: 1
Reputation: 14224
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]
res5: X = A@f0ad2ea
Upvotes: 1