Search code examples
scalagenericsstructural-typing

Collection structural type parameter weirdness


This seems like a simple thing but I can't understand it...

This compiles:

object CanFoo1 {
  def foo(): Unit = {
    println("Yup, I can foo alright")
  }
}

object CanFoo2 {
  def foo(): Unit = {
    println("And I can foo with the best")
  }
}

trait A {
  type CanFoo = { def foo(): Unit }
  def fooers: Seq[CanFoo]
}

class B extends A {
  def fooers = Seq(
    // CanFoo1, // <- won't compile when this is uncommented
    CanFoo2
  )
}

But uncommenting the // CanFoo1, line gives:

error: type mismatch;
found   : Seq[Object]
required: Seq[B.this.CanFoo]
   (which expands to)  Seq[AnyRef{def foo(): Unit}]
def fooers = Seq(
              ^
one error found

So it seems like the compiler understands that a collection containing just one element Seq(CanFoo2) (or Seq(CanFoo1)) is of the correct type, but when both objects are in the collection it gives up? What am I doing wrong here?


Solution

  • So it seems like the compiler understands that a collection containing just one element Seq(CanFoo2) (or Seq(CanFoo1)) is of the correct type, but when both objects are in the collection it gives up? What am I doing wrong here?

    When you pass CanFoo1 or CanFoo2 to the Seq apply, the sequence is inferred to be of type CanFoo1.type or CanFoo2.type respectively, it is not inferred to be of type CanFoo.

    When you pass in both elements to the Seq, the compiler tries to look for a common type to which it can validly infer to make the code compile, and the only type it can find is Object, but fooers is said to be of type Seq[CanFoo], so the compiler yells.

    You can help the compiler a little by explicitly writing the type of the collection:

    class B extends A {
      def fooers = Seq[CanFoo](
        CanFoo1,
        CanFoo2
      )
    }