Scala: expression type incompatible with formal parameter type
trait Ingredient{}
case class Papperoni() extends Ingredient{}
case class Mushroom() extends Ingredient{}
trait ToppingDef[T] {
}
object PepperoniDef extends Serializable with ToppingDef[Papperoni] {
}
object MushroomDef extends Serializable with ToppingDef[Mushroom] {
}
class Oven[T <: Ingredient](val topping:ToppingDef[T]) {
}
class Pizza {
def cook = {
val topping =
if(someCondition()) { PepperoniDef }
else { MushroomDef}
new Oven(topping) // <-- build error here
}
}
I am using Scala 2.11. This example is somewhat contrived, but I've separated all unrelated ones to give a concise example.
Error on the last line:
Error:(26, 5) no type parameters for constructor Oven: (topping: ToppingDef[T])Oven[T] exist so that it can be applied to arguments (Serializable with ToppingDef[_ >: Papperoni with Mushroom <: Product with Serializable with Ingredient])
--- because ---
argument expression type is not compatible with formal parameter type;
found : Serializable with ToppingDef[_ >: Papperoni with Mushroom <: Product with Serializable with Ingredient]
required: ToppingDef[?T]
new Oven(topping)
However, changing the last line to this, for example:
new Oven(PepperoniDef)
builds perfectly. Therefore, the compiler has no problem finding the type when the parameter is passed explicitly like this.
Also, remove the trait Serializable
from PepperoniDef
and MushroomDef
as follows:
object PepperoniDef extends ToppingDef[Papperoni] {
}
object MushroomDef extends ToppingDef[Mushroom] {
}
also builds. However, in my case, I need Serializable.
I think that maybe I can rebuild the code to work around this if needed, but I would like to understand what is going on, I do not know why this type is ambiguous in the first case or why the presence of the Serializable symbol has any effect ... Thanks in advance for any ideas.
EDIT: Thanks for the answers, very helpful. I think the most concise solution is to change this:
val topping =
:
val topping:ToppingDef[_ <: Ingredient] =
Which cures the build error and doesn't need to change for the generic classes, which I would like to keep as simple as possible, without constraints, so that Scala will output as much type information as possible.
This does not answer the question of why presence Serializable
influences this.
source to share
It seems that helping the compiler with type annotation makes this compilation:
val topping: ToppingDef[
_ >: Papperoni with Mushroom <: Ingredient with Product with Serializable] =
if (true) {
PepperoniDef
} else {
MushroomDef
}
I don't think it has to do with the class Serializable
, in particular, it looks like the compiler is quirky about me since the produced type has a mixed type, including Product with Serializable
anyway.
You can also "relax" the type signature by doing T
covariant, which means Topping[Ingredient]
. This is because the "subtype" relation of the relation Papperoni <: Ingredient
to the covariant ToppingDef[+T]
means ToppingDef[Papperoni] <: ToppingDef[Ingredient]
that it allows a common supertype to be used for T
:
trait ToppingDef[+T]
val topping: ToppingDef[Ingredient with Product with Serializable] =
if (true) {
PepperoniDef
} else {
MushroomDef
}
And this also compiles without type annotation.
Edit:
Creating a type parameter Oven
means that an existential, not a generic quantized type, also works with a trait Serializable
:
class Oven[_ <: Ingredient](val topping: ToppingDef[_])
val topping: Serializable with ToppingDef[_ <: Ingredient] =
if (true) {
PepperoniDef
} else {
MushroomDef
}
new Oven(topping)
source to share