Skip to content

Commit 3648180

Browse files
committed
Simplify Enums
I had a lingering unease that the rules for enums were too complex, and the syntax too cumbersome. I now think I have found a way to simplify things considerably. The key was to take away one capabilitiy: that cases can have bodies which can define members. Arguably, if we choose an ADT decompositon of a problem it's good style to write all methods using pattern matching instead of overriding individual cases. So this removes an unnecessary choice. What's more, once we have eliminated case bodies we also have eliminated scope confusion. All that remains are the case parameters and extends clause. Extends clauses of cases can be handled like supercalls in constructors: I.e. the enclosing enum class is not visible for them. This means we can treat enums unequivocally as classes. They can have methods and other statements just like other classes can. Cases in enums are seen as a form of constructors. We do not need a distinction between enum class and enum object anymore. Enums can have companion objects just like normal classes can, of course. This also means that type parameters of enums scope naturally over cases, just like they scope over secondary constructors. We do not need to repeat them in cases anymore, which is a huge relief. This first commit changes the syntax and docs. It still needs to be implemented.
1 parent 3365ed3 commit 3648180

File tree

12 files changed

+142
-168
lines changed

12 files changed

+142
-168
lines changed

docs/docs/internals/syntax.md

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -290,16 +290,6 @@ AccessQualifier ::= ‘[’ (id | ‘this’) ‘]’
290290
291291
Annotation ::= ‘@’ SimpleType {ParArgumentExprs} Apply(tpe, args)
292292
293-
TemplateBody ::= [nl] ‘{’ [SelfType] TemplateStat {semi TemplateStat} ‘}’ (self, stats)
294-
TemplateStat ::= Import
295-
| {Annotation [nl]} {Modifier} Def
296-
| {Annotation [nl]} {Modifier} Dcl
297-
| EnumCaseStat
298-
| Expr1
299-
|
300-
SelfType ::= id [‘:’ InfixType] ‘=>’ ValDef(_, name, tpt, _)
301-
| ‘this’ ‘:’ InfixType ‘=>’
302-
303293
Import ::= ‘import’ ImportExpr {‘,’ ImportExpr}
304294
ImportExpr ::= StableId ‘.’ (id | ‘_’ | ImportSelectors) Import(expr, sels)
305295
ImportSelectors ::= ‘{’ {ImportSelector ‘,’} (ImportSelector | ‘_’) ‘}’
@@ -335,19 +325,14 @@ DefDef ::= DefSig [‘:’ Type] ‘=’ Expr
335325
| ‘this’ DefParamClause DefParamClauses DefDef(_, <init>, Nil, vparamss, EmptyTree, expr | Block)
336326
(‘=’ ConstrExpr | [nl] ConstrBlock)
337327
338-
TmplDef ::= ([‘case’ | `enum'] ‘class’ | trait’) ClassDef
328+
TmplDef ::= ([‘case’] ‘class’ | trait’) ClassDef
339329
| [‘case’] ‘object’ ObjectDef
340330
| `enum' EnumDef
341331
ClassDef ::= id ClassConstr TemplateOpt ClassDef(mods, name, tparams, templ)
342332
ClassConstr ::= [ClsTypeParamClause] [ConstrMods] ClsParamClauses with DefDef(_, <init>, Nil, vparamss, EmptyTree, EmptyTree) as first stat
343333
ConstrMods ::= {Annotation} [AccessModifier]
344334
ObjectDef ::= id TemplateOpt ModuleDef(mods, name, template) // no constructor
345-
EnumDef ::= id ClassConstr [`extends' [ConstrApps]] EnumDef(mods, name, tparams, template)
346-
[nl] ‘{’ EnumCaseStat {semi EnumCaseStat} ‘}’
347-
EnumCaseStat ::= {Annotation [nl]} {Modifier} EnumCase
348-
EnumCase ::= `case' (EnumClassDef | ObjectDef | ids)
349-
EnumClassDef ::= id [ClsTpeParamClause | ClsParamClause] ClassDef(mods, name, tparams, templ)
350-
ClsParamClauses TemplateOpt
335+
EnumDef ::= id ClassConstr [`extends' [ConstrApps]] EnumBody EnumDef(mods, name, tparams, template)
351336
TemplateOpt ::= [‘extends’ Template | [nl] TemplateBody]
352337
Template ::= ConstrApps [TemplateBody] | TemplateBody Template(constr, parents, self, stats)
353338
ConstrApps ::= ConstrApp {‘with’ ConstrApp}
@@ -357,6 +342,20 @@ ConstrExpr ::= SelfInvocation
357342
SelfInvocation ::= ‘this’ ArgumentExprs {ArgumentExprs}
358343
ConstrBlock ::= ‘{’ SelfInvocation {semi BlockStat} ‘}’
359344
345+
TemplateBody ::= [nl] ‘{’ [SelfType] TemplateStat {semi TemplateStat} ‘}’ (self, stats)
346+
TemplateStat ::= Import
347+
| {Annotation [nl]} {Modifier} Def
348+
| {Annotation [nl]} {Modifier} Dcl
349+
| Expr1
350+
|
351+
SelfType ::= id [‘:’ InfixType] ‘=>’ ValDef(_, name, tpt, _)
352+
| ‘this’ ‘:’ InfixType ‘=>’
353+
354+
EnumBody ::= [nl] ‘{’ [SelfType] EnumStat {semi EnumStat} ‘}’
355+
EnumStat ::= TemplateStat
356+
| {Annotation [nl]} {Modifier} EnumCase
357+
EnumCase ::= `case' (id [ClsTpeParamClause] {ClsParamClause} | ids)
358+
360359
TopStatSeq ::= TopStat {semi TopStat}
361360
TopStat ::= {Annotation [nl]} {Modifier} TmplDef
362361
| Import

docs/docs/reference/enums/adts.md

Lines changed: 27 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -10,28 +10,28 @@ how an `Option` type can be represented as an ADT:
1010

1111
```scala
1212
enum Option[+T] {
13-
case Some[+T](x: T)
13+
case Some(x: T)
1414
case None
1515
}
1616
```
1717

18-
This example introduces `Option` enum class with a covariant type
19-
parameter `T`, together with two cases, `Some` and `None`. `Some` is
20-
parameterized with a type parameter `T` and a value parameter `x`. It
21-
is a shorthand for writing a case class that extends `Option`. Since
22-
`None` is not parameterized it is treated as a normal enum value.
18+
This example introduces an `Option` enum with a covariant type
19+
parameter `T` consisting of two cases, `Some` and `None`. `Some` is
20+
parameterized with a value parameter `x`. It is a shorthand for writing a
21+
case class that extends `Option`. Since `None` is not parameterized, it
22+
is treated as a normal enum value.
2323

2424
The `extends` clauses that were omitted in the example above can also
2525
be given explicitly:
2626

2727
```scala
2828
enum Option[+T] {
29-
case Some[+T](x: T) extends Option[T]
30-
case None extends Option[Nothing]
29+
case Some(x: T) extends Option[T]
30+
case None extends Option[Nothing]
3131
}
3232
```
3333

34-
Note that the parent type of `None` is inferred as
34+
Note that the parent type of the `None` value is inferred as
3535
`Option[Nothing]`. Generally, all covariant type parameters of the enum
3636
class are minimized in a compiler-generated extends clause whereas all
3737
contravariant type parameters are maximized. If `Option` was non-variant,
@@ -59,24 +59,22 @@ scala> new Option.Some(2)
5959
val res3: t2.Option.Some[Int] = Some(2)
6060
```
6161

62-
As all other enums, ADTs can have methods on both the enum class and
63-
its companion object. For instance, here is `Option` again, with an
64-
`isDefined` method and an `Option(...)` constructor.
62+
As all other enums, ADTs can define methods. For instance, here is `Option` again, with an
63+
`isDefined` method and an `Option(...)` constructor in its companion object.
6564

6665
```scala
67-
enum class Option[+T] {
68-
def isDefined: Boolean
66+
enum Option[+T] {
67+
case Some(x: T) extends Option[T]
68+
case None
69+
70+
def isDefined: Boolean = this match {
71+
case None => false
72+
case some => true
73+
}
6974
}
7075
object Option {
7176
def apply[T >: Null](x: T): Option[T] =
7277
if (x == null) None else Some(x)
73-
74-
case Some[+T](x: T) {
75-
def isDefined = true
76-
}
77-
case None {
78-
def isDefined = false
79-
}
8078
}
8179
```
8280

@@ -98,23 +96,20 @@ enum Color(val rgb: Int) {
9896

9997
### Syntax of Enums
10098

101-
Changes to the syntax fall in two categories: enum classes and cases inside enums.
99+
Changes to the syntax fall in two categories: enum definitions and cases inside enums.
102100
The changes are specified below as deltas with respect to the Scala syntax given [here](http://dotty.epfl.ch/docs/internals/syntax.html)
103101

104-
1. Enum definitions and enum classes are defined as follows:
102+
1. Enum definitions are defined as follows:
105103

106-
TmplDef ::= `enum' `class’ ClassDef
107-
| `enum' EnumDef
108-
EnumDef ::= id ClassConstr [`extends' [ConstrApps]]
109-
[nl] `{’ EnumCaseStat {semi EnumCaseStat} `}’
104+
TmplDef ::= `enum' EnumDef
105+
EnumDef ::= id ClassConstr [`extends' [ConstrApps]] EnumBody
106+
EnumBody ::= [nl] ‘{’ [SelfType] EnumStat {semi EnumStat} ‘}’
107+
EnumStat ::= TemplateStat
108+
| {Annotation [nl]} {Modifier} EnumCase
110109

111110
2. Cases of enums are defined as follows:
112111

113-
EnumCaseStat ::= {Annotation [nl]} {Modifier} EnumCase
114-
EnumCase ::= `case' (EnumClassDef | ObjectDef | ids)
115-
EnumClassDef ::= id [ClsTpeParamClause | ClsParamClause]
116-
ClsParamClauses TemplateOpt
117-
TemplateStat ::= ... | EnumCaseStat
112+
EnumCase ::= `case' (id [ClsTpeParamClause] {ClsParamClause} | ids)
118113

119114
### Reference
120115

docs/docs/reference/enums/desugarEnums.md

Lines changed: 64 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -3,111 +3,109 @@ layout: doc-page
33
title: "Translation of Enums and ADTs"
44
---
55

6-
The compiler expands enum classes and cases to code that only uses
6+
The compiler expands enums and their cases to code that only uses
77
Scala's other language features. As such, enums in Scala are
88
convenient _syntactic sugar_, but they are not essential to understand
99
Scala's core.
1010

1111
We now explain the expansion of enums in detail. First,
1212
some terminology and notational conventions:
1313

14-
- We use `E` as a name of an enum class, and `C` as a name of an enum case that appears in the companion object of `E`.
15-
- We use `<...>` for syntactic constructs that in some circumstances might be empty. For instance `<body>` represents either the body of a case between `{...}` or nothing at all.
14+
- We use `E` as a name of an enum, and `C` as a name of a case that appears in `E`.
15+
- We use `<...>` for syntactic constructs that in some circumstances might be empty. For instance,
16+
`<value-params>` represents one or more a parameter lists `(...)` or nothing at all.
1617

1718
- Enum cases fall into three categories:
1819

1920
- _Class cases_ are those cases that are parameterized, either with a type parameter section `[...]` or with one or more (possibly empty) parameter sections `(...)`.
20-
- _Simple cases_ are cases of a non-generic enum class that have neither parameters nor an extends clause or body. That is, they consist of a name only.
21+
- _Simple cases_ are cases of a non-generic enum that have neither parameters nor an extends clause or body. That is, they consist of a name only.
2122
- _Value cases_ are all cases that do not have a parameter section but that do have a (possibly generated) extends clause and/or a body.
2223

2324
Simple cases and value cases are collectively called _singleton cases_.
2425

2526
The desugaring rules imply that class cases are mapped to case classes, and singleton cases are mapped to `val` definitions.
2627

27-
There are eight desugaring rules. Rules (1) and (2) desugar enums and
28-
enum classes. Rules (3) and (4) define extends clauses for cases that
29-
are missing them. Rules (5 - 7) define how such expanded cases map
30-
into case classes, case objects or vals. Finally, rule (8) expands
31-
comma separated simple cases into a sequence of cases.
28+
There are eight desugaring rules. Rule (1) desugar enum definitions. Rules
29+
(2) and (3) desugar simple cases. Rules (4) to (6) define extends clauses for cases that
30+
are missing them. Rules (7) and (8) define how such cases with extends clauses
31+
map into case classes or vals.
3232

3333
1. An `enum` definition
3434

35-
enum E ... { <cases> }
35+
enum E ... { <defs> <cases> }
3636

37-
expands to an enum class and a companion object
37+
expands to a `sealed` `abstract` class that extends the `scala.Enum` trait and
38+
an associated companion object that contains the defined cases, expanded according
39+
to rules (2 - 8).
3840

39-
enum class E ...
40-
object E { <cases> }
41+
sealed abstract class E ... extends <parents> with scala.Enum { <defs> }
42+
object E { <cases> }
4143

42-
2. An enum class definition
44+
2. A simple case consisting of a comma-separated list of enum names
4345

44-
enum class E ... extends <parents> ...
46+
case C_1, ..., C_n
4547

46-
expands to a `sealed` `abstract` class that extends the `scala.Enum` trait:
48+
expands to
4749

48-
sealed abstract class E ... extends <parents> with scala.Enum ...
50+
case C_1; ...; case C_n
4951

50-
3. If `E` is an enum class without type parameters, then a case in its companion object without an extends clause
52+
Any modifiers or annotations on the original case extend to all expanded
53+
cases.
5154

52-
case C <params> <body>
55+
3. A simple case
5356

54-
expands to
57+
case C
5558

56-
case C <params> <body> extends E
59+
of an enum `E` that does not take type parameters expands to
5760

58-
4. If `E` is an enum class with type parameters `Ts`, then a case in its
59-
companion object without an extends clause
61+
val C = $new(n, "C")
6062

61-
case C <params> <body>
63+
Here, `$new` is a private method that creates an instance of of `E` (see
64+
below).
6265

63-
expands according to two alternatives, depending whether `C` has type
64-
parameters or not. If `C` has type parameters, they must have the same
65-
names and appear in the same order as the enum type parameters `Ts`
66-
(variances may be different, however). In this case
66+
4. If `E` is an enum with type parameters
6767

68-
case C [Ts] <params> <body>
68+
V1 T1 > L1 <: U1 , ... , Vn Tn >: Ln <: Un (n > 0)
6969

70-
expands to
70+
where each of the variances `Vi` is either `'+'` or `'-'`, then a simple case
7171

72-
case C[Ts] <params> extends E[Ts] <body>
72+
case C
7373

74-
For the case where `C` does not have type parameters, assume `E`'s type
75-
parameters are
74+
expands to
7675

77-
V1 T1 > L1 <: U1 , ... , Vn Tn >: Ln <: Un (n > 0)
76+
case C extends E[B1, ..., Bn]
7877

79-
where each of the variances `Vi` is either `'+'` or `'-'`. Then the case
80-
expands to
78+
where `Bi` is `Li` if `Vi = '+'` and `Ui` if `Vi = '-'`. This result is then further
79+
rewritten with rule (7). Simple cases of enums with non-variant type
80+
parameters are not permitted.
8181

82-
case C <params> extends E[B1, ..., Bn] <body>
82+
5. A class case without an extends clause
8383

84-
where `Bi` is `Li` if `Vi = '+'` and `Ui` if `Vi = '-'`. It is an error if
85-
`Bi` refers to some other type parameter `Tj (j = 0,..,n-1)`. It is also
86-
an error if `E` has type parameters that are non-variant.
84+
case C <type-params> <value-params>
8785

88-
5. A class case
86+
of an enum `E` that does not take type parameters expands to
8987

90-
case C <params> ...
88+
case C <type-params> <value-params> extends E
9189

92-
expands analogous to a final case class:
90+
This result is then further rewritten with rule (8).
9391

94-
final case class C <params> ...
92+
6. If `E` is an enum with type parameters `Ts`, a class case with neither type parameters nor
93+
an extends clause
9594

96-
However, unlike for a regular case class, the return type of the associated
97-
`apply` method is a fully parameterized type instance of the enum class `E`
98-
itself instead of `C`. Also the enum case defines an `enumTag` method of
99-
the form
95+
case C <value-params>
10096

101-
def enumTag = n
97+
expands to
10298

103-
where `n` is the ordinal number of the case in the companion object,
104-
starting from 0.
99+
case C[Ts] <value-params> extends E[Ts]
105100

106-
6. A value case
101+
This result is then further rewritten with rule (8). For class cases that have type parameters
102+
themselves, an extends clause needs to be given explicitly.
107103

108-
case C extends <parents> <body>
104+
7. A value case
109105

110-
expands to a value definition
106+
case C extends <parents>
107+
108+
expands to a value definition in `E`'s companion object:
111109

112110
val C = new <parents> { <body>; def enumTag = n; $values.register(this) }
113111

@@ -116,27 +114,24 @@ comma separated simple cases into a sequence of cases.
116114
as one of the `enumValues` of the enumeration (see below). `$values` is a
117115
compiler-defined private value in the companion object.
118116

119-
7. A simple case
120-
121-
case C
117+
8. A class case
122118

123-
of an enum class `E` that does not take type parameters expands to
124-
125-
val C = $new(n, "C")
119+
case C <params> extends <parents>
126120

127-
Here, `$new` is a private method that creates an instance of of `E` (see
128-
below).
121+
expands analogous to a final case class in `E`'s companion object:
129122

130-
8. A simple case consisting of a comma-separated list of enum names
123+
final case class C <params> extends <parents>
131124

132-
case C_1, ..., C_n
125+
However, unlike for a regular case class, the return type of the associated
126+
`apply` method is a fully parameterized type instance of the enum class `E`
127+
itself instead of `C`. Also the enum case defines an `enumTag` method of
128+
the form
133129

134-
expands to
130+
def enumTag = n
135131

136-
case C_1; ...; case C_n
132+
where `n` is the ordinal number of the case in the companion object,
133+
starting from 0.
137134

138-
Any modifiers or annotations on the original case extend to all expanded
139-
cases.
140135

141136
### Equality
142137

@@ -149,7 +144,7 @@ be compared only to other values of the same enum type. Furtermore, generic
149144

150145
### Translation of Enumerations
151146

152-
Non-generic enum classes `E` that define one or more singleton cases
147+
Non-generic enums `E` that define one or more singleton cases
153148
are called _enumerations_. Companion objects of enumerations define
154149
the following additional members.
155150

0 commit comments

Comments
 (0)