Search code examples
scalagenericstypescompilationstructural-typing

Scala: F-Bounded Polymorphism Woes


Assume the existence of the following types and method:

trait X[A <: X[A]]

case class C extends X[C]

def m(x: PartialFunction[X[_], Boolean])

I want to be able to create a PartialFunction to be passed into m.

A first attempt would be to write

val f: PartialFunction[X[_], Boolean] = { 
  case c: C => true
}

m(f)

This fails with type arguments [_$1] do not conform to trait X's type parameter bounds [A <: X[A]]. So, it seems we have to constraint X's type parameters.

A second attempt:

val f: PartialFunction[{type A <: X[A]}, Boolean] = { 
  case c: C => true
}

m(f)

This fails on the application of m because PartialFunction[AnyRef{type A <: X[this.A]},Boolean] <: PartialFunction[X[_],Boolean] is false.

Is there any way not involving casting that actually satisfies the compiler both on the definition of the partial function and on the application of m?


Solution

  • I'm not sure what exactly you want, but since you are using an existential type (in disguise of the _ syntax), this is how you can make that work:

    val f: PartialFunction[X[A] forSome {type A <: X[A]}, Boolean] = {
      case c : C => true
    }
    

    The _ syntax isn't good enough here, since you need to give the existential type the right upper bound. That is only possible with the more explicit forSome syntax.

    What I find surprising, though, is that Scala accepts the declaration

    def m(x: PartialFunction[X[_], Boolean])
    

    in the first place. It seems weird that it even considers X[_] a well-formed type. This is short for X[A] forSome {type A <: Any}, which should not be a valid application of X, because it does not conform to the parameter bounds.