aboutsummaryrefslogtreecommitdiff
path: root/docs/HigherKinded-v2.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/HigherKinded-v2.md')
-rw-r--r--docs/HigherKinded-v2.md375
1 files changed, 0 insertions, 375 deletions
diff --git a/docs/HigherKinded-v2.md b/docs/HigherKinded-v2.md
deleted file mode 100644
index 2ca6424de..000000000
--- a/docs/HigherKinded-v2.md
+++ /dev/null
@@ -1,375 +0,0 @@
-Higher-Kinded Types in Dotty V2
-===============================
-
-This note outlines how we intend to represent higher-kinded types in
-Dotty. The principal idea is to collapse the four previously
-disparate features of refinements, type parameters, existentials and
-higher-kinded types into just one: refinements of type members. All
-other features will be encoded using these refinements.
-
-The complexity of type systems tends to grow exponentially with the
-number of independent features, because there are an exponential
-number of possible feature interactions. Consequently, a reduction
-from 4 to 1 fundamental features achieves a dramatic reduction of
-complexity. It also adds some nice usablilty improvements, notably in
-the area of partial type application.
-
-This is a second version of the scheme which differs in a key aspect
-from the first one: Following Adriaan's idea, we use traits with type
-members to model type lambdas and type applications. This is both more
-general and more robust than the intersections with type constructor
-traits that we had in the first version.
-
-The duality
------------
-
-The core idea: A parameterized class such as
-
- class Map[K, V]
-
-is treated as equivalent to a type with type members:
-
- class Map { type Map$K; type Map$V }
-
-The type members are name-mangled (i.e. `Map$K`) so that they do not conflict with other
-members or parameters named `K` or `V`.
-
-A type-instance such as `Map[String, Int]` would then be treated as equivalent to
-
- Map { type Map$K = String; type Map$V = Int }
-
-Named type parameters
----------------------
-
-Type parameters can have unmangled names. This is achieved by adding the `type` keyword
-to a type parameter declaration, analogous to how `val` indicates a named field. For instance,
-
- class Map[type K, type V]
-
-is treated as equivalent to
-
- class Map { type K; type V }
-
-The parameters are made visible as fields.
-
-Wildcards
----------
-
-A wildcard type such as `Map[_, Int]` is equivalent to
-
- Map { type Map$V = Int }
-
-I.e. `_`'s omit parameters from being instantiated. Wildcard arguments
-can have bounds. E.g.
-
- Map[_ <: AnyRef, Int]
-
-is equivalent to
-
- Map { type Map$K <: AnyRef; type Map$V = Int }
-
-
-Type parameters in the encodings
---------------------------------
-
-The notion of type parameters makes sense even for encoded types,
-which do not contain parameter lists in their syntax. Specifically,
-the type parameters of a type are a sequence of type fields that
-correspond to parameters in the unencoded type. They are determined as
-follows.
-
- - The type parameters of a class or trait type are those parameter fields declared in the class
- that are not yet instantiated, in the order they are given. Type parameter fields of parents
- are not considered.
- - The type parameters of an abstract type are the type parameters of its upper bound.
- - The type parameters of an alias type are the type parameters of its right hand side.
- - The type parameters of every other type is the empty sequence.
-
-Partial applications
---------------------
-
-The definition of type parameters in the previous section leads to a simple model of partial applications.
-Consider for instance:
-
- type Histogram = Map[_, Int]
-
-`Histogram` is a higher-kinded type that still has one type parameter.
-`Histogram[String]`
-would be a possible type instance, and it would be equivalent to `Map[String, Int]`.
-
-
-Modelling polymorphic type declarations
----------------------------------------
-
-The partial application scheme gives us a new -- and quite elegant --
-way to do certain higher-kinded types. But how do we interprete the
-poymorphic types that exist in current Scala?
-
-More concretely, current Scala allows us to write parameterized type
-definitions, abstract types, and type parameters. In the new scheme,
-only classes (and traits) can have parameters and these are treated as
-equivalent to type members. Type aliases and abstract types do not
-allow the definition of parameterized types so we have to interprete
-polymorphic type aliases and abstract types specially.
-
-Modelling polymorphic type aliases: simple case
------------------------------------------------
-
-A polymorphic type alias such as
-
- type Pair[T] = Tuple2[T, T]
-
-where `Tuple2` is declared as
-
- class Tuple2[T1, T2] ...
-
-is expanded to a monomorphic type alias like this:
-
- type Pair = Tuple2 { type Tuple2$T2 = Tuple2$T1 }
-
-More generally, each type parameter of the left-hand side must
-appear as a type member of the right hand side type. Type members
-must appear in the same order as their corresponding type parameters.
-References to the type parameter are then translated to references to the
-type member. The type member itself is left uninstantiated.
-
-This technique can expand most polymorphic type aliases appearing
-in Scala codebases but not all of them. For instance, the following
-alias cannot be expanded, because the parameter type `T` is not a
-type member of the right-hand side `List[List[T]]`.
-
- type List2[T] = List[List[T]]
-
-We scanned the Scala standard library for occurrences of polymorphic
-type aliases and determined that only two occurrences could not be expanded.
-In `io/Codec.scala`:
-
- type Configure[T] = (T => T, Boolean)
-
-And in `collection/immutable/HashMap.scala`:
-
- private type MergeFunction[A1, B1] = ((A1, B1), (A1, B1)) => (A1, B1)
-
-For these cases, we use a fall-back scheme that models a parameterized alias as a
-`Lambda` type.
-
-Modelling polymorphic type aliases: general case
-------------------------------------------------
-
-A polymorphic type alias such as
-
- type List2D[T] = List[List[T]]
-
-is represented as a monomorphic type alias of a type lambda. Here's the expanded version of
-the definition above:
-
- type List2D = Lambda$I { type Apply = List[List[$hkArg$0]] }
-
-Here, `Lambda$I` is a standard trait defined as follows:
-
- trait Lambda$I[type $hkArg$0] { type +Apply }
-
-The `I` suffix of the `Lambda` trait indicates that it has one invariant type parameter (named $hkArg$0).
-Other suffixes are `P` for covariant type parameters, and `N` for contravariant type parameters. Lambda traits can
-have more than one type parameter. For instance, here is a trait with contravariant and covariant type parameters:
-
- trait Lambda$NP[type -$hkArg$0, +$hkArg1] { type +Apply } extends Lambda$IP with Lambda$NI
-
-Aside: the `+` prefix in front of `Apply` indicates that `Apply` is a covariant type field. Dotty
-admits variance annotations on type members.
-
-The definition of `Lambda$NP` shows that `Lambda` traits form a subtyping hierarchy: Traits which
-have covariant or contravariant type parameters are subtypes of traits which don't. The supertraits
-of `Lambda$NP` would themselves be written as follows.
-
- trait Lambda$IP[type $hkArg$0, +$hkArg1] { type +Apply } extends Lambda$II
- trait Lambda$NI[type -$hkArg$0, $hkArg1] { type +Apply } extends Lambda$II
- trait Lambda$II[type $hkArg$0, $hkArg1] { type +Apply }
-
-`Lambda` traits are special in that
-they influence how type applications are expanded: If the standard type application `T[X1, ..., Xn]`
-leads to a subtype `S` of a type instance
-
- LambdaXYZ { type Arg1 = T1; ...; type ArgN = Tn; type Apply ... }
-
-where all argument fields `Arg1, ..., ArgN` are concretely defined
-and the definition of the `Apply` field may be either abstract or concrete, then the application
-is further expanded to `S # Apply`.
-
-For instance, the type instance `List2D[String]` would be expanded to
-
- Lambda$I { type $hkArg$0 = String; type Apply = List[List[String]] } # Apply
-
-which in turn simplifies to `List[List[String]]`.
-
-2nd Example: Consider the two aliases
-
- type RMap[K, V] = Map[V, K]
- type RRMap[K, V] = RMap[V, K]
-
-These expand as follows:
-
- type RMap = Lambda$II { self1 => type Apply = Map[self1.$hkArg$1, self1.$hkArg$0] }
- type RRMap = Lambda$II { self2 => type Apply = RMap[self2.$hkArg$1, self2.$hkArg$0] }
-
-Substituting the definition of `RMap` and expanding the type application gives:
-
- type RRMap = Lambda$II { self2 => type Apply =
- Lambda$II { self1 => type Apply = Map[self1.$hkArg$1, self1.$hkArg$0] }
- { type $hkArg$0 = self2.$hkArg$1; type $hkArg$1 = self2.$hkArg$0 } # Apply }
-
-Substituting the definitions for `self1.$hkArg${1,2}` gives:
-
- type RRMap = Lambda$II { self2 => type Apply =
- Lambda$II { self1 => type Apply = Map[self2.$hkArg$0, self2.$hkArg$1] }
- { type $hkArg$0 = self2.$hkArg$1; type $hkArg$1 = self2.$hkArg$0 } # Apply }
-
-Simplifiying the `# Apply` selection gives:
-
- type RRMap = Lambda$II { self2 => type Apply = Map[self2.$hkArg$0, self2.$hkArg$1] }
-
-This can be regarded as the eta-expanded version of `Map`. It has the same expansion as
-
- type IMap[K, V] = Map[K, V]
-
-
-Modelling higher-kinded types
------------------------------
-
-The encoding of higher-kinded types uses again the `Lambda` traits to represent type constructors.
-Consider the higher-kinded type declaration
-
- type Rep[T]
-
-We expand this to
-
- type Rep <: Lambda$I
-
-The type parameters of `Rep` are the type parameters of its upper bound, so
-`Rep` is a unary type constructor.
-
-More generally, a higher-kinded type declaration
-
- type T[v1 X1 >: S1 <: U1, ..., vn XN >: S1 <: UN] >: SR <: UR
-
-is encoded as
-
- type T <: LambdaV1...Vn { self =>
- type v1 $hkArg$0 >: s(S1) <: s(U1)
- ...
- type vn $hkArg$N >: s(SN) <: s(UN)
- type Apply >: s(SR) <: s(UR)
- }
-
-where `s` is the substitution `[XI := self.$hkArg$I | I = 1,...,N]`.
-
-If we instantiate `Rep` with a type argument, this is expanded as was explained before.
-
- Rep[String]
-
-would expand to
-
- Rep { type $hkArg$0 = String } # Apply
-
-If we instantiate the higher-kinded type with a concrete type constructor (i.e. a parameterized
-trait or class), we have to do one extra adaptation to make it work. The parameterized trait
-or class has to be eta-expanded so that it comforms to the `Lambda` bound. For instance,
-
- type Rep = Set
-
-would expand to
-
- type Rep = Lambda1 { type Apply = Set[$hkArg$0] }
-
-Or,
-
- type Rep = Map[String, _]
-
-would expand to
-
- type Rep = Lambda1 { type Apply = Map[String, $hkArg$0] }
-
-
-Full example
-------------
-
-Consider the higher-kinded `Functor` type class
-
- class Functor[F[_]] {
- def map[A, B](f: A => B): F[A] => F[B]
- }
-
-This would be represented as follows:
-
- class Functor[F <: Lambda1] {
- def map[A, B](f: A => B): F { type $hkArg$0 = A } # Apply => F { type $hkArg$0 = B } # Apply
- }
-
-The type `Functor[List]` would be represented as follows
-
- Functor {
- type F = Lambda1 { type Apply = List[$hkArg$0] }
- }
-
-Now, assume we have a value
-
- val ml: Functor[List]
-
-Then `ml.map` would have type
-
- s(F { type $hkArg$0 = A } # Apply => F { type $hkArg$0 = B } # Apply)
-
-where `s` is the substitution of `[F := Lambda1 { type Apply = List[$hkArg$0] }]`.
-This gives:
-
- Lambda1 { type Apply = List[$hkArg$0] } { type $hkArg$0 = A } # Apply
- => Lambda1 { type Apply = List[$hkArg$0] } { type $hkArg$0 = B } # Apply
-
-This type simplifies to:
-
- List[A] => List[B]
-
-Status of #
------------
-
-In the scheme above we have silently assumed that `#` "does the right
-thing", i.e. that the types are well-formed and we can collapse a type
-alias with a `#` projection, thereby giving us a form of beta
-reduction.
-
-In Scala 2.x, this would not work, because `T#X` means `x.X forSome { val x: T }`.
-Hence, two occurrences of `Rep[Int]` say, would not be recognized to be equal because the
-existential would be opened each time afresh.
-
-In pre-existentials Scala, this would not have worked either. There, `T#X` was a fundamental
-type constructor, but was restricted to alias types or classes for both `T` and `X`.
-Roughly, `#` was meant to encode Java's inner classes. In Java, given the classes
-
- class Outer { class Inner }
- class Sub1 extends Outer
- class Sub2 extends Outer
-
-The types `Outer#Inner`, `Sub1#Inner` and `Sub2#Inner` would all exist and be
-regarded as equal to each other. But if `Outer` had abstract type members this would
-not work, since an abstract type member could be instantiated differently in `Sub1` and `Sub2`.
-Assuming that `Sub1#Inner = Sub2#Inner` could then lead to a soundness hole. To avoid soundness
-problems, the types in `X#Y` were restricted so that `Y` was (an alias of) a class type and
-`X` was (an alias of) a class type with no abstract type members.
-
-I believe we can go back to regarding `T#X` as a fundamental type constructor, the way it
-was done in pre-existential Scala, but with the following relaxed restriction:
-
- _In a type selection `T#x`, `T` is not allowed to have any abstract members different from `X`._
-
-This would typecheck the higher-kinded types examples, because they only project with `# Apply` once all
-`$hkArg$` type members are fully instantiated.
-
-It would be good to study this rule formally, trying to verify its soundness.
-
-
-
-
-
-
-
-