Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala types: least upper bounds

I am trying to parameterize some methods with very general type parameters.

As an example, in the REPL I first define:

trait Term
case class FunctionalTerm[+T <: Term](t: T) extends Term

Intuitively, the following method takes a Term and a FunctionalTerm, and returns something with type the least upper bound of the type of term passed and the argument type of the FunctionalTerm:

def ex1[T1 <: Term, T3 <: X, FunctionalTerm[T1] <: X, X <: R, R <: Term](t1: FunctionalTerm[T1], s: T3): R = sys.error("TODO")

So far so good in the REPL.

Then I define ex2 as a convenience function that performs the same operation as ex1, but with the input arguments swapped:

def ex2[T2 <: Term, T3 <: X, FunctionalTerm[T2] <: X, X <: R, R <: Term](s: T3, t2: FunctionalTerm[T2]): R = ex1(t2,s)

Attempting to define ex2 in the REPL gives the following error:

error: inferred type arguments [T2,T3,FunctionalTerm,T3,T3] do not conform to method ex1's type parameter bounds [T1 <: Term,T3 <: X,FunctionalTerm[T1] <: X,X <: R,R <: Term]
         ex1(t2,s)
         ^
error: type mismatch;
 found   : FunctionalTerm[T2]
 required: FunctionalTerm[T1]
         ex1(t2,s)
             ^
error: type mismatch;
 found   : T3(in method ex2)
 required: T3(in method ex1)
         ex1(t2,s)
                ^
error: type mismatch;
 found   : R(in method ex1)
 required: R(in method ex2)
         ex1(t2,s)
            ^

I have spent about two days trying to figure out a solution, and am now totally stuck. I cannot find anything more on Google.

Since the type argument list of ex2 is the same as that of ex1 but with T1 and T2 swapped, I don't understand is wrong, or how to fix it.

Any help would be hugely appreciated!

Update

Least upper bounds were a red herring. The example can be distilled further.

The following two functions can be defined in the REPL without error:

def ex1[T1 <: Term, FunctionalTerm[T1] <: Term](t1: FunctionalTerm[T1]): Term = sys.error("TODO: ex1")
def ex2[T2 <: Term, FunctionalTerm[T2] <: Term](t2: FunctionalTerm[T2]): Term = ex1(t2)

Introducing the extra parameter X seems to cause the problem. I can define the following in the REPL:

def ex3[T1 <: Term, FunctionalTerm[T1] <: X, X <: Term](t1: FunctionalTerm[T1]): Term = sys.error("TODO: ex3")

But attempting to subsequently define:

def ex4[T2 <: Term, FunctionalTerm[T2] <: X, X <: Term](t2: FunctionalTerm[T2]): Term = ex3(t2)

gives the error:

error: inferred type arguments [T2,FunctionalTerm,Nothing] do not conform to method ex3's type parameter bounds [T1 <: Term,FunctionalTerm[T1] <: X,X <: Term]
       def ex4[T2 <: Term, FunctionalTerm[T2] <: X, X <: Term](t2: FunctionalTerm[T2]): Term = ex3(t2)
                                                                                               ^
error: type mismatch;
 found   : FunctionalTerm[T2]
 required: FunctionalTerm[T1]
       def ex4[T2 <: Term, FunctionalTerm[T2] <: X, X <: Term](t2: FunctionalTerm[T2]): Term = ex3(t2)
                                                                                                   ^

So, I guess the question becomes: Why does the parameter X unused in the signature have this effect?

like image 593
Andrew Bate Avatar asked May 16 '12 17:05

Andrew Bate


People also ask

What is Supertype in Scala?

Scala Type HierarchyAny is the supertype of all types, also called the top type. It defines certain universal methods such as equals , hashCode , and toString . Any has two direct subclasses: AnyVal and AnyRef . AnyVal represents value types.

What are type bounds?

In Scala, Type Bounds are restrictions on Type Parameters or Type Variable. By using Type Bounds, we can define the limits of a Type Variable.

What does <: mean in Scala?

It means an abstract type member is defined (inside some context, e.g. a trait or class), so that concrete implementations of that context must define that type.

What are type parameters in Scala?

Methods in Scala can be parameterized by type as well as by value. The syntax is similar to that of generic classes. Type parameters are enclosed in square brackets, while value parameters are enclosed in parentheses.


1 Answers

I'm not sure whether or not you're experiencing a bug, but I am pretty sure you're making your life a lot more difficult that it needs to be. You can rely on covariance and unification to do all the hard work for you,

scala> trait Term
defined trait Term

scala> case class FunctionalTerm[+T <: Term](t: T) extends Term
defined class FunctionalTerm

scala> def ex1[T <: Term](t1 : FunctionalTerm[T], s : T) : T = s
ex1: [T <: Term](t1: FunctionalTerm[T], s: T)T

scala> class A extends Term ; class B extends A ; class C extends A
defined class A
defined class B
defined class C
scala> ex1(new FunctionalTerm(new B), new C)
res0: A = C@4ceeb514

Note the inferred result type (which is equivalent to the R of your original more complex definition) ... it's A which is the LUB of B and C.

Now the flipped version is trivial and Just Works,

scala> def ex2[T <: Term](s : T, t1 : FunctionalTerm[T]) : T = s
ex2: [T <: Term](s: T, t1: FunctionalTerm[T])T

scala> ex2(new C, new FunctionalTerm(new B))
res1: A = C@522ddcec 
like image 109
Miles Sabin Avatar answered Oct 06 '22 02:10

Miles Sabin