Search code examples
scalagenericstypeerrorhigher-kinded-types

Type error because identical looking type members are somehow different?


This is a highly simplified version of some code I'm working on for my library, for which I'm getting a type error that I don't understand. My class library supports the "map" function, which uses builders similar to how the Scala's own collection library uses builders (I've stripped the builders out of my example here for simplicity, as it's not necessary to reproduce the error).

class MultiSignal[T] {
  def map[U, R[_] <: MultiSignal[_]](f : T => U) : R[U] =
    throw new Exception()  //not implemented in this version
}

class OrderedMultiSignal[T] extends MultiSignal[T] {
  type Position
}

object orderedMultiSignal extends OrderedMultiSignal[Int] {
  type Position = Int
}

object MyApp {
  //type error on next line
  val mappedSignal : OrderedMultiSignal[String] { type Position = Int } = orderedMultiSignal.map[String, OrderedMultiSignal { type Position = Int }](_.toString)
} 

When I compile this code with the -uniqid parameter, I get this error:

Resource    Description Path    Location    Type
SameType.scala  type mismatch;
  found   : OrderedMultiSignal#7774[String#7331]{type Position#25375 = Int#1076}
  required: OrderedMultiSignal#7774[String#7331]{type Position#25335 = Int#1076}

As you can see, the types are identical except that somehow the Position type member is considered different, even though they have the same name and the same value. How can this be? Can I modify the way I specify the OrderedMultiSignal { type Position = Int } type parameter to address this?

Update

After experimenting a bit more it occurred to me to try a type lambda expression, and it worked. In the line with the type error, replace OrderedMultiSignal { type Position = Int } with ({type λ[α] = OrderedMultiSignal[α] { type Position = Int }})#λ and it works. I would still like to understand why my original syntax does not work, though.


Solution

  • The OrderedMultiSignal higher-kinded type doesn't have a Position type member, because it isn't a subtype of OrderedMultiSignal[something]. So when you write OrderedMultiSignal { type Position = Int }, you refine OrderedMultiSignal by adding a new type member also called Position but not related to the one in OrderedMultiSignal[A].

    Or in other words, OrderedMultiSignal { type Position = Int } is equivalent to ({type λ[α] = OrderedMultiSignal[α]})#λ { type Position = Int } instead of the type you ended up with.