Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding “inferred type arguments do not conform to type parameter bounds” errors in Scala

I fail to understand why I am getting an “inferred type arguments do not conform to type parameter bounds”. First, I defined a trait called CS which may be implemented by several classes (e.g., CS01 and CS02):

trait CS[+T <: CS[T]] {
  this: T =>
  def add: T
  def remove: T
}

class CS01 extends CS[CS01] {
  def add: CS01 = new CS01
  def remove: CS01 = new CS01
}

class CS02 extends CS[CS02] {
  def add: CS02 = new CS02
  def remove: CS02 = new CS02
}

The idea is to keep the implemented type when calling add or remove on CS01 and CS02. Secondly, I would like to define operations that may be executed on every classes compliant with trait CS. Then, I defined a trait called Exec(with two very simple examples of classes Exec01 and Exec02 mixin the Exec traits):

trait Exec {
  def exec[U <: CS[U]](x: U): U
}

class Exec01 extends Exec {
  def exec[U <: CS[U]](x: U): U = x.add
}

class Exec02 extends Exec {
  def exec[U <: CS[U]](x: U): U = x.remove
}

Once again, I need to keep the implemented type of the class that mixes the CS trait. That is why exec is parametrized with [U <: CS[U]].

Finally, I want any CS enabling operations on it to mixin the trait Executable which makes it possible to execute an operation that follows trait Exec:

trait Executable[T <: CS[T]] {
  this: T =>
  def execute(e: Exec): T = e.exec(this)
}

However, I get the following error when I am trying to compile:

error: inferred type arguments [this.Executable[T] with T] do not conform to method exec's type parameter bounds [U <: this.CS[U]]
  def execute(e: Exec): T = e.exec(this)
                              ^

I don't quite understand because any classes that mix Executable must be of type T with the constraint of mixin the CS trait due to the bound in trait Executable[T <: CS[T]]. So, why this does not conform to the type parameter bound U <: CS[U] ?

like image 201
GDD Avatar asked Jan 11 '13 13:01

GDD


2 Answers

Works if you specify the type parameter to exec explicitly:

def execute(e: Exec): T = e.exec[T](this)

Seems to be a limitation in the type inference.

like image 152
Jesper Nordenberg Avatar answered Nov 15 '22 22:11

Jesper Nordenberg


Disclaimer: not a scala guru here, I'm learning it as I'm writing this.

First, let's simplify the example.

scala> trait Moo[+X <: Moo[X]] 
defined trait Moo

scala> class Foo extends Moo[Foo]
defined class Foo

scala> def foobar[U <: Moo[U]](x: U) = x
foobar: [U <: Moo[U]](x: U)U

scala> foobar(new Foo)
res0: Foo = Foo@191275b

scala> class Bar extends Foo
defined class Bar

scala> foobar(new Bar)
<console>:12: error: inferred type arguments [Bar] do not conform to method 
foobar's type parameter bounds [U <: Moo[U]]
              foobar(new Bar)
              ^

scala> 

foobar accepts a Foo argument but rejects a Bar which only extends Foo. Why? foobar is a generic, paramaterized by the type of its argument. It imposes a bound on that type. The type inferencer will not check each and every ancestor of the argument type, hoping to find one that satisfies the bound.

So how to impose a bound on an ancestor type? One method is with existential types.

scala> def foobar[V <: Moo[U] forSome {type U}](x: V) = x
foobar: [U <: Moo[_], V <: U](x: V)V

scala> foobar(new Foo)
res3: Foo = Foo@1154718

scala> foobar(new Bar)
res4: Bar = Bar@5a7ff7

scala> 
like image 32
n. 1.8e9-where's-my-share m. Avatar answered Nov 15 '22 21:11

n. 1.8e9-where's-my-share m.