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]
?
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.
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>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With