erdavila
erdavila

Reputation: 371

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
}

Upvotes: 0

Views: 60

Answers (3)

erdavila
erdavila

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

nikhil
nikhil

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

Kolmar
Kolmar

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

Related Questions