Home > Software design >  Are assignments implicitly covariant?
Are assignments implicitly covariant?

Time:01-26

This must be a very basic misunderstanding on my part. It appears that assignments of parametric types are covariant without any indication on my part that that's what I'd want. I'm pasting Scala code for brevity, but it behaves identically in Java.

class Pet
class Fish extends Pet
class Guppy extends Fish
case class Box[T](value: T)
val guppyBox: Box[Fish] = Box(new Guppy()) // Mysteriously, this works.

An instance of type X can only be assigned to a val of type Y if Y is a subtype of X. In my case, this would require Box to be covariant, which I didn't say it is.

I wouldn't be too hung up on this, but it leads to the following odd, in my view, behavior:

  def unboxFish(fish: Box[Fish]) = ???

  unboxFish(Box(new Guppy()))       // Oddly, compiles ok
  val guppyBox2 = Box(new Guppy())
  unboxFish(guppyBox2)              // The compilation error I'd expect.

Any help greatly appreciated!

CodePudding user response:

Box isn't covariant. What's happening here is that Box(new Guppy()) needs to infer the type parameter for Box, and the inference depends on context. When you do

val guppyBox2 = Box(new Guppy())

it infers the type parameter as Guppy, but when you do

val guppyBox: Box[Fish] = Box(new Guppy())

the compiler knows that the RHS should be a Box[Fish], so it infers that the type parameter should be Fish instead of Guppy, as if you had written

val guppyBox: Box[Fish] = Box[Fish](new Guppy())

CodePudding user response:

In Scala type inference goes not only from right to left

val guppyBox: Box[??] = Box[Something](...)

but also from left to right

val guppyBox: Box[Something] = Box[??](...)

(so it's bidirectional).

So in

val guppyBox: Box[Fish] = Box(new Guppy())

aka

val guppyBox: Box[Fish] = Box[??](new Guppy())

the type parameter ?? is inferred to be Fish.

when does it need explicit type when declare variable in scala?

But Box is now not covariant. Box[Guppy] is not a subtype of Box[Fish]

implicitly[Box[Guppy] <:< Box[Fish]] // doesn't compile

You can't assign a value of type Box[Guppy] to a variable of type Box[Fish]

val guppyBox: Box[Fish] = Box[Guppy](new Guppy()) // doesn't compile
  • Related