-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Spec: Rewrite the type system fundamentals. #17447
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There are two fundamental differences between the type sytems of Scala 2 and 3, which made a fundamental rewrite desirable: - Scala 2 defines path-dependent types in terms of type projections, whereas Scala 3 is path-dependent first, and has not general type projects. - Scala 2 has ad hod higher-kinded type parameters and polymorphic type aliases, whereas Scala 3 has first-class type lambdas and is fundamentally higher order. We therefore rewrite the entire type system section of the spec to be higher-kinded and path-dependent first. In the process, we also revise refined types to better fit the path-dependent nature. I (sjrd) took the liberty to add myself as a contributor in the index page of the spec, as I believe this constitutes a fundamental contribution to the language specification.
And better define what a "type definition" is.
To align with the rest of the spec, which is syntax-first.
d7ea469
to
6021b7b
Compare
@odersky I have updated the spec according to what we discussed last week. In particular, its starts with the concrete syntax of types now, and the desugaring of type lambdas and refined types is more explicit. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the new structure. It's a great improvement to what we had before.
docs/_spec/03-types.md
Outdated
## Paths | ||
In this specification, we specify and use types in terms of the following _abstract syntax_. | ||
The abstract syntax does not directly correspond to concrete syntax. | ||
It abstracts away irrelevant details such as precedence and grouping, and contains shapes of types that cannot be directly expressed using the concrete syntax. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: Mark shapes that cannot be expressed in syntax specially (maybe in Italics? or else in a comment at the line's end.)
docs/_spec/03-types.md
Outdated
|
||
### Type Constructors | ||
|
||
To each type constructor corresponds an _inferred type parameter clause_ which is computed as follow: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To each type constructor corresponds an _inferred type parameter clause_ which is computed as follow: | |
To each type constructor corresponds an _inferred type parameter clause_ which is computed as follows: |
docs/_spec/03-types.md
Outdated
```ebnf | ||
Type ::= FunctionArgTypes ‘=>’ Type | ||
FunctionArgTypes ::= InfixType | ||
| ‘(’ [ ParamType {‘,’ ParamType } ] ‘)’ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dependent function types are missing here. The reference syntax is actually:
FunTypeArgs ::= InfixType
| ‘(’ [ FunArgTypes ] ‘)’
| FunParamClause
FunParamClause ::= ‘(’ TypedFunParam {‘,’ TypedFunParam } ‘)’
TypedFunParam ::= id ‘:’ Type
docs/_spec/03-types.md
Outdated
| TypeLambdaParams '=>' Type | ||
FunctionArgTypes ::= InfixType | ||
| ‘(’ [ FunArgTypes ] ‘)’ | ||
| FunParamClause |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FunParamClause is not further explained. We need to explain that , e.g.
(x: T) => x.M
expands to
Function1[T, U] { def apply(x: T): x.M }
assuming that U
is the least supertype of x.M
that does not mention x
.
Aside: This encoding might change. We'd really like it to be something like
DependentFunction1 { def apply(x: T): x.M }
instead.
docs/_spec/03-types.md
Outdated
SimpleType ::= Path ‘.’ ‘type’ | ||
Type ::= FunctionArgTypes ‘=>’ Type | ||
FunctionArgTypes ::= InfixType | ||
| ‘(’ [ ParamType {‘,’ ParamType } ] ‘)’ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The syntax summary gives a different expansion for FunctionArgTypes
. These need to be aligned.
- Otherwise, if either ´M_1´ or ´M_2´ is a class result, the result is that one. | ||
- Otherwise, ´M_1´ and ´M_2´ must either both be term results or both be type results. | ||
- If they are term results with underlying types ´U_1´ and ´U_2´ and stable flags ´s_1´ and ´s_2´, the result is a term result whose underlying type is `meet(´U_1´, ´U_2´)` and whose stable flag is ´s_1 \lor s_2´. | ||
- If they are type results with underlying type definitions ´D_1´ and ´D_2´, the result is a type result whose underlying type definition is `intersect(´D_1´, ´D_2´)`. | ||
|
||
## Relations between types |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Equivalence meaning "interchangeable in all contexts" is prone to mislead. Also because of its triple equals sign. It's definitely not structural equivalence. So in the table below I would put conformance first, and then define equivalence as mutual conformance. Also, if possible use another symbol like =:=
or and =
with a ~
on top.
docs/_spec/03-types.md
Outdated
- ´\sigma (>: M_i <: G_i)´ is contained in ´>: L_i <: H_i´ (i.e., ´L_i <: \sigma M_i´ and ´\sigma G_i <: H_i´). | ||
- ´S = p.X´ and `´T = [\pm b_1 >: M_1 <: G_1, ..., \pm b_n >: M_n <: G_n]´ =>> ´T_1´` and ´S´ is a type constructor with ´n´ type parameters and: | ||
- `´([\pm a_1 >: L_1 <: H_1, ..., \pm a_n >: L_n <: H_n]´ =>> ´S[a_1, ..., a_n]) <: T´` where the ´a_i´ are copies of the type parameters of ´S´ (i.e., we can eta-expand ´S´ to compare it to a type lambda). | ||
- `´T = T_1´ { ´R´ }` and ´S <: T´ and ´S´ is a stable type ´p´ (TODO: what if it is not stable?) and: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we solve that by skolemization. What happens is that we compute the member type of R in T_1. That goes through an asSeenFrom. The asSeenFrom will skolemize when we have to replace a C.this
type with a non-stable prefix. So maybe we should add that case to the definition of asSeenFrom.
Compare with the treatment of QualSkolemType in TypeOps.asSeenFrom.
- ´p´ is ´S´ if ´S´ is a stable type and ´∃ \alpha : S´ otherwise, and | ||
- ´p'´ is the result of replacing any top-level recursive type `{ ´\gamma´ => ´Z´ }` in ´p´ with ´[\gamma := p]Z´ (TODO specify this better). | ||
- `´S = (´=> ´S_1)´` and `´T = (´=> ´T_1)´` and ´S_1 <: T_1´. | ||
- `´S =´ scala.Null` and: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be good to also say what happens under -YexplicitNulls here.
docs/_spec/03-types.md
Outdated
|
||
We define `matches(´S´, ´T´)` where ´S´ and ´T´ are types or methodic types as: | ||
|
||
<!-- TODO We should mention parameter substitution here. --> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, we should do that.
- A type alias `type ´t´[´T_1´, ..., ´T_n´] = ´T´` subsumes a type alias `type ´t´[´T_1´, ..., ´T_n´] = ´T'´` if ´T \equiv T'´. | ||
- A type declaration `type ´t´[´T_1´, ..., ´T_n´] >: ´L´ <: ´U´` subsumes a type declaration `type ´t´[´T_1´, ..., ´T_n´] >: ´L'´ <: ´U'´` if ´L' <: L´ and ´U <: U'´. | ||
- A type or class definition that binds a type name ´t´ subsumes an abstract type declaration `type t[´T_1´, ..., ´T_n´] >: L <: U` if ´L <: t <: U´. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Further below: I think we should drop the "Weak Conformance" section 3.6.3. Then in 3.6.4 we should refer to numeric widening as an implicit conversion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The section on Volatile types should be dropped. It is replaced by a notion of realizability.
The section on Erasure should be updated to the new rules.
Generally, all usages of compound types (using with
) should be dropped.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Weak conformance will be the subject of a separate PR that introduces harmonization, so I would rather not touch it now.
I updated erasure. For the volatile types I'll have to investigate how realizability affects that section.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the new structure. It's a great improvement to what we had before.
I think I have addressed everything so far except the Volatile Types -> Realizable comment. |
Volatility now replaced by realizability as well. (also updated the PDF) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks all good now.
There are two fundamental differences between the type sytems of Scala 2 and 3, which made a fundamental rewrite desirable:
We therefore rewrite the entire type system section of the spec to be higher-kinded and path-dependent first. In the process, we also revise refined types to better fit the path-dependent nature.
I (sjrd) took the liberty to add myself as a contributor in the index page of the spec, as I believe this constitutes a fundamental contribution to the language specification.
The rendered version of the Types chapter can be seen here:
Types Scala 3.pdf(old version)Types Scala 3.pdf