Open
Description
In the code below, I'd hope for Client(Server0)
to be accepted, without the need for eta-expansion. Achieving this appears important in the literature on path-dependent types.
trait MapImpl {
type Key
type Value
type Map
val lookup: Map => Key => Value
}
import scala.collection.immutable.HashMap
class HashMapImpl[K, V] extends MapImpl {
type Key = K
type Value = V
type Map = HashMap[K, V]
val lookup: Map => Key => Value = m => k => m(k)
}
object Foo {
val Server0:
(mImpl: MapImpl) => mImpl.Map => mImpl.Key => mImpl.Value
= mImpl => mImpl.lookup
val Client:
(server: (mImpl: MapImpl & {type Key = String} & {type Value = Int}) => mImpl.Map => String => Int) => Int =
server => server(HashMapImpl[String, Int])(HashMap())("test lookup key") //works
// server => server(??? : (HashMapImpl[String, Int]))(???)("test lookup key") //works
// server => server(???)(???)("test lookup key") // crashes
val Server1: (mImpl: MapImpl & {type Key = String} & {type Value = Int}) => mImpl.Map => String => Int =
mImpl => Server0(mImpl)
val Result1: Int = Client(Server0) // rejected
val Result2: Int = Client(Server1) // accepted
val Result3: Int = Client(mImpl => Server0(mImpl)) // accepted
}
gives:
-- [E007] Type Mismatch Error: /Users/pgiarrusso/git/dotty-example-project/src/main/scala/foo.scala:27:28
27 | val Result1: Int = Client(Server0) // rejected
| ^^^^^^^
|Found: (mImpl: MapImpl) => (mImpl.Map => mImpl.Key => mImpl.Value)(Foo.Server0)
|Required: (mImpl: MapImpl & Object{Key = String} & Object{Value = Int}) => mImpl.Map =>
| String
| => Int
one error found
BTW, inspecting source code suggests a potential similar problem for methods: matchingMethodParams(tp1, tp2)
only performs dependent substitution on tp2
, but here we need "dependent substitution" on tp1
as well.