Starting Scala 3 existential types have been dropped and one of the reasons is stated as
Existential types largely overlap with path-dependent types, so the gain of having them is relatively minor.
Given "largely", so not always, I was wondering if a concrete example can be provided demonstrating how to rewrite an existential type as path-dependent type, and an example when such replacement is not possible?
Suppose that
Tis the type we want to bind by the existential quantifier, andF[T]is some type that depends onT, so thatis our existential type.
Providing an instance of type A means that:
tthat can be bound byTF[t]But we could just as well put both components into a single type, and make
Ta path-dependent type member:Instances of type
Bare described by same data:tthat can be bound by the nameT.F[t]The values of both
AandBcarry around roughly the same information, the main difference is how we can access the typeTover which we are quantifying. In case ofb: B, we can get it as path-dependent typep.T, whereas fora: A, we could use type-variables in pattern matching.Here is an example that demonstrates how to map between the existentially quantified and the path dependent types:
(this compiles on 2.13)
I would guess that the "largely" is there, because unlike Scala 3 / Dotty, previous versions of Scala did not have any rigorously formalized foundations, so maybe the author of this sentence just did not want to invoke the impression that every existential type from 2.13 could be exactly represented by path dependent types, because such a claim could not be made rigorous anyway.