scalapath-dependent-typetype-projection

Type mismatch with type projection


I'm having a compiler type mismatch error that I do not understand.

Given the following definition of an Elem and a factory (Companion):

object Elem {
  trait Companion[E[~] <: Elem[~]] {
    def apply[S](peer: E[S]#Peer): E[S] // wrap a peer in its elem
  }
}
trait Elem[S] {
  type Peer
}

And given for example an Obj with attributes and a peer type Expr:

trait Expr[S, A]

trait Obj[S] {
  // query an attribute by key
  def attr[E[~] <: Elem[~]](key: String)
                           (implicit c: Elem.Companion[E]): Option[E[S]#Peer]
}

I should be able to do the following:

// process elements with peer `Expr[~, A]`
trait Impl[S, A, E[~] <: Elem[~] { type Peer = Expr[~, A] }] {
  implicit def companion: Elem.Companion[E]

  def test(obj: Obj[S]): Unit =
    obj.attr[E]("foo").fold(()) { ex =>
      val newElem = companion[S](ex)
    }
}

This last bit fails with the brilliant error message:

<console>:62: error: type mismatch; found : E[S]#Peer (which expands to) Expr[S,A] required: E[S]#Peer (which expands to) Expr[S,A] val newElem = companion[S](ex) ^


Solution

  • I reduced your example to the following:

    trait Elem {
        type Peer
    }
    
    trait Impl[E[~] <: Elem {type Peer = ~}] {
        def foo[R](peer: E[R]#Peer)
    
        foo[Int](??? : E[Int]#Peer)
    }
    

    where scalac gives

    Error:(12, 18) type mismatch;
     found   : this.scala.Peer
        (which expands to)  Int
     required: this.Peer(in method foo)
        (which expands to)  R
        foo[Int](??? : E[Int]#Peer)
                 ^^
    

    I'm not sure, but this looks like a Scala bug, since R isn't even in scope at the site of the error -- it seems to have leaked out. Scala seems to be failing to perform the substitution R = Int inside the refinement, allowing the parameter R to remain.