Search code examples
scalaexistential-typehigher-kinded-types

Higher-Kinded existential type


I've been mulling over a design problem in a library I'm working on, and I realized that using existential types may allow me to change my design in a way that simplifies many parts of my library. However, I can't quite seem to get it to work.

It seems to me that myBuilder conforms to the type MultiSignalBuilder[E, R] forSome { type E[+X] >: Element[X] }, where Element[X] is MultiSignalElement[X], but the compiler says it does't. It seems to have to do the fact that E is a higher-kinded type. Why doesn't this work, and is there a way to fix it?

  class MultiSignalElement[+T] {
  }

  abstract class MultiSignal[+T] {
    type Element[+X] <: MultiSignalElement[X]

    val element : Element[T]

    def transform[R[+X] <: MultiSignal[X]](builder : MultiSignalBuilder[E, R] forSome { type E[+X] >: Element[X] }) : R[T] =
      builder.buildNew(element)
  }

  abstract class MultiSignalBuilder[-E[+X] <: MultiSignalElement[X], +R[+X] <: MultiSignal[X]] {
    def buildNew[T](element : E[T]) : R[T]
  }

  object myBuilder extends MultiSignalBuilder[MultiSignalElement, MultiSignal] {
    def buildNew[T](e : MultiSignalElement[T]) = new MultiSignal[T]() {
      type Element[+X] = MultiSignalElement[X]

      val element = e
    }
  }

  val multiSignal = new MultiSignal[Int] {
    type Element[+X] = MultiSignalElement[X]

    val element = new MultiSignalElement()
  }

  multiSignal.transform(myBuilder) //type error on this line
  multiSignal.transform[MultiSignal](myBuilder) //type error on this line

Solution

  • Let do step-by-step analysis.

    First we have

    def transform[R](builder : MultiSignalBuilder[E, R] forSome { type E[+X] >: Element[X] }) : Unit = { }
    

    Which is equivalent to statement : there exists

    type E[+X] >: Element[X]
    

    For which we can define

    def transform[E[+X] >: Element[X], R[+_]](builder : MultiSignalBuilder[E, R] ) : Unit = { }
    

    Here we have an error

    Error:(7, 18) covariant type X occurs in contravariant position in type [+X] >: MultiSignal.this.Element[X] of type E

    This is something. You are expecting your mysterious existential covariant type should be a supertype of another covariant type. I think this is the first thing which is freaking the compiler. Lets change relation to subtyping

    def transform[E[+X] <: Element[X], R[+_]](builder : MultiSignalBuilder[E, R] ) : Unit = { }
    

    Now we have

    Error:(7, 56) type arguments [E,R] do not conform to class MultiSignalBuilder's type parameter bounds [-E[+X] <: MultiSignalElement[X],+R[+X] <: MultiSignal[X]]

    So we forgot to require subtyping of MultiSignal[X] out of R parameter.

    Lets change it

     def transform[E[+X] <: Element[X], R[+X] <: MultiSignal[X]](builder : MultiSignalBuilder[E, R] ) : Unit = { }
    

    Now

    multiSignal.transform[MultiSignalElement,MultiSignal](myBuilder)
    

    Is succesfully compiled.

    Finally we could get back to existential version

    def transform[R[+X] <: MultiSignal[X]](builder : MultiSignalBuilder[E, R] forSome {type E[+X] <: Element[X]}) : Unit = { }
    

    With which

    multiSignal.transform[MultiSignal](myBuilder)
    

    Is succesfully compiled.

    Sadly

    multiSignal.transform(myBuilder)
    

    Still is not compiled. I think there is too much type relations to resolve for the compiler.