Skip to content

Commit f8cb74e

Browse files
authored
Integrate the enums reference into the spec (#17392)
Work by @bishabosha
2 parents ec3cdad + 94a0fcd commit f8cb74e

File tree

7 files changed

+459
-452
lines changed

7 files changed

+459
-452
lines changed

docs/_spec/05-classes-and-objects.md

Lines changed: 341 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ The list of parents of a template must be well-formed.
4141
This means that the class denoted by the superclass constructor ´sc´ must be a subclass of the superclasses of all the traits ´mt_1, ..., mt_n´.
4242
In other words, the non-trait classes inherited by a template form a chain in the inheritance hierarchy which starts with the template's superclass.
4343

44+
It is forbidden for a template's superclass constructor ´sc´ to be an [enum class](#enum-definitions), unless the template is the implementation of an [enum case](#enum-definitions) of ´sc´.
45+
4446
The _least proper supertype_ of a template is the class type or [compound type](03-types.html#compound-types) consisting of all its parent class types.
4547

4648
The statement sequence ´\mathit{stats}´ contains member definitions that define new members or overwrite members in the parent classes.
@@ -801,3 +803,342 @@ Generally, a _companion module_ of a class is an object which has the same name
801803
Conversely, the class is called the _companion class_ of the module.
802804

803805
Very much like a concrete class definition, an object definition may still contain declarations of abstract type members, but not of abstract term members.
806+
807+
## Enum Definitions
808+
809+
<!-- TODO: Agree with NTs of rest of spec -->
810+
```ebnf
811+
TmplDef ::= ‘enum’ EnumDef
812+
EnumDef ::= id ClassConstr [‘extends’ [ConstrApps]] EnumBody
813+
EnumBody ::= [nl] ‘{’ [SelfType] EnumStat {semi EnumStat} ‘}’
814+
EnumStat ::= TemplateStat
815+
| {Annotation [nl]} {Modifier} EnumCase
816+
EnumCase ::= ‘case’ (id ClassConstr [‘extends’ ConstrApps] | ids)
817+
```
818+
819+
An _enum definition_ implies the definition of an _enum class_, a companion object, and one or more _enum cases_.
820+
821+
Enum definitions are useful to encode both Generalised Algebraic Data Types and Enumerated Types.
822+
823+
The compiler expands enum definitions to code that only uses Scala's other language features.
824+
As such, enum definitions in Scala are convenient _syntactic sugar_, but they are not essential to understand Scala's core.
825+
826+
We now explain the expansion of enum definitions in detail.
827+
First, some terminology and notational conventions:
828+
829+
- We use ´E´ as a name of an enum definition, and ´C´ as a name of an enum case that appears in ´E´.
830+
- We use `<...>` for syntactic constructs that in some circumstances might be empty.
831+
For instance, `<value-params>` represents one or more parameter lists `(´\mathit{ps}_1\,´)...(´\mathit{ps}_n´)` or nothing at all.
832+
- Enum classes fall into two categories:
833+
- _parameterized_ enum classes have at least one of the following:
834+
- a type parameter section, denoted as `[´\mathit{tps}\,´]`;
835+
- one or more (possibly empty) parameter sections, denoted as `(´\mathit{ps}_1\,´)...(´\mathit{ps}_n´)`.
836+
- _unparameterized_ enum classes have no type parameter sections and no parameter sections.
837+
- Enum cases fall into three categories:
838+
839+
- _Class cases_ are those cases that are parameterized, either with a type parameter section `[´\mathit{tps}\,´]` or with one or more (possibly empty) parameter sections `(´\mathit{ps}_1\,´)...(´\mathit{ps}_n´)`.
840+
- _Simple cases_ are cases of an unparameterized enum that have neither parameters nor an extends clause or body.
841+
That is, they consist of a name only.
842+
- _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.
843+
844+
- Simple cases and value cases are collectively called _singleton cases_.
845+
846+
###### Example
847+
848+
An example enum for a `Planet` enumeration can be given as
849+
```scala
850+
enum Planet(mass: Double, radius: Double):
851+
case Mercury extends Planet(3.303e+23, 2.4397e6)
852+
case Venus extends Planet(4.869e+24, 6.0518e6)
853+
case Earth extends Planet(5.976e+24, 6.37814e6)
854+
case Mars extends Planet(6.421e+23, 3.3972e6)
855+
case Jupiter extends Planet(1.9e+27, 7.1492e7)
856+
case Saturn extends Planet(5.688e+26, 6.0268e7)
857+
case Uranus extends Planet(8.686e+25, 2.5559e7)
858+
case Neptune extends Planet(1.024e+26, 2.4746e7)
859+
860+
private inline val G = 6.67300E-11
861+
def surfaceGravity = G * mass / (radius * radius)
862+
def surfaceWeight(otherMass: Double) = otherMass * surfaceGravity
863+
end Planet
864+
```
865+
866+
###### Example
867+
868+
An example enum for the Option ADT can be given as
869+
```scala
870+
enum Option[+T]:
871+
case Some(x: T)
872+
case None
873+
```
874+
875+
### Lowering of Enum Definitions
876+
877+
###### Summary
878+
An enum class is represented as a `sealed` class that extends the `scala.reflect.Enum` trait.
879+
880+
Enum cases are represented as follows:
881+
- a class case is mapped to a `case class`,
882+
- a singleton case is mapped to a `val` definition, where
883+
- Simple cases all share a single implementation class.
884+
- Value cases will each be implemented by a unique class.
885+
886+
###### Precise rules
887+
The `scala.reflect.Enum` trait defines a single public method, `ordinal`:
888+
```scala
889+
package scala.reflect
890+
891+
transparent trait Enum extends Any, Product, Serializable:
892+
893+
def ordinal: Int
894+
```
895+
There are nine desugaring rules.
896+
Rule (1) desugars enum definitions.
897+
Rules (2) and (3) desugar simple cases.
898+
Rules (4) to (6) define `extends` clauses for cases that are missing them.
899+
Rules (7) to (9) define how such cases with `extends` clauses map into `case class`es or `val`s.
900+
901+
1. An `enum` definition
902+
```scala
903+
enum ´E´ ... { <defs> <cases> }
904+
```
905+
expands to a `sealed abstract` class that extends the `scala.reflect.Enum` trait and an associated companion object that contains the defined cases, expanded according to rules (2 - 8).
906+
The enum class starts with a compiler-generated import that imports the names `<caseIds>` of all cases so that they can be used without prefix in the class.
907+
```scala
908+
sealed abstract class ´E´ ... extends <parents> with scala.reflect.Enum {
909+
import ´E´.{ <caseIds> }
910+
<defs>
911+
}
912+
object ´E´ { <cases> }
913+
```
914+
915+
2. A singleton case consisting of a comma-separated list of enum names
916+
```scala
917+
case ´C_1´, ..., ´C_n´
918+
```
919+
expands to
920+
```scala
921+
case ´C_1´; ...; case ´C_n´
922+
```
923+
Any modifiers or annotations on the original case extend to all expanded cases.
924+
This result is then further rewritten by either (3 or 4).
925+
926+
3. A singleton case without an extends clause
927+
```scala
928+
case ´C´
929+
```
930+
of an unparameterized enum `´E´` expands to the following simple enum case in `´E´`'s companion object:
931+
```scala
932+
val ´C´ = $new(n, "C")
933+
```
934+
Here, `$new` is a private method that creates an instance of ´E´ (see below).
935+
936+
4. A singleton case without an extends clause
937+
```scala
938+
case ´C´
939+
```
940+
of an enum `´E´` with type parameters
941+
```scala
942+
´\mathit{v}_1´ ´T_1´ >: ´L_1´ <: ´U_1´ , ... , ´\mathit{v}_n´ ´T_n´ >: ´L_n´ <: ´U_n´ (n > 0)
943+
```
944+
where each of the variances `´\mathit{v}_i´` is either `'+'` or `'-'`, expands to the following value enum case:
945+
```scala
946+
case ´C´ extends ´E´[´B_1´, ..., ´B_n´]
947+
```
948+
where `´B_i´` is `´L_i´` if `´\mathit{v}_i´ = '+'` and `´U_i´` if `´\mathit{v}_i´ = '-'`.
949+
This result is then further rewritten with rule (8).
950+
**NOTE:** It is not permitted for enums with non-variant type parameters to have singleton cases without an extends clause.
951+
952+
5. A class case without an extends clause
953+
```scala
954+
case ´C´ <type-params> <value-params>
955+
```
956+
of an enum `´E´` that does not take type parameters expands to
957+
```scala
958+
case ´C´ <type-params> <value-params> extends ´E´
959+
```
960+
This result is then further rewritten with rule (9).
961+
962+
6. If `´E´` is an enum with type parameters `´\mathit{tps}´`, a class case with neither type parameters nor an extends clause
963+
```scala
964+
case ´C´ <value-params>
965+
```
966+
expands to
967+
```scala
968+
case ´C´[´\mathit{tps}´] <value-params> extends ´E´[´\mathit{tps}´]
969+
```
970+
This result is then further rewritten with rule (9).
971+
For class cases that have type parameters themselves, an extends clause needs to be given explicitly.
972+
973+
974+
7. If `´E´` is an enum with type parameters `´\mathit{tps}´`, a class case without type parameters but with an extends clause
975+
```scala
976+
case ´C´ <value-params> extends <parents>
977+
```
978+
expands to
979+
```scala
980+
case ´C´[´\mathit{tps}´] <value-params> extends <parents>
981+
```
982+
provided at least one of the parameters `´\mathit{tps}´` is mentioned in a parameter type in `<value-params>` or in a type argument in `<parents>`.
983+
984+
8. A value case
985+
```scala
986+
case ´C´ extends <parents>
987+
```
988+
expands to the following `val` definition in `´E´`'s companion object:
989+
```scala
990+
val ´C´ = new <parents> { <body>; def ordinal = ´\mathit{n}´ }
991+
```
992+
where `´\mathit{n}´` is the ordinal number of the case in the companion object, starting from 0.
993+
The anonymous class also implements the abstract `Product` methods that it inherits from `Enum`.
994+
**NOTE:** It is an error if a value case refers to a type parameter of `´E´` in a type argument within `<parents>`.
995+
996+
9. A class case
997+
```scala
998+
case ´C´ <type-params> <value-params> extends <parents>
999+
```
1000+
expands analogous to a final case class in `´E´`'s companion object:
1001+
```scala
1002+
final case class ´C´ <type-params> <value-params> extends <parents> {
1003+
def ordinal = ´\mathit{n}´
1004+
}
1005+
```
1006+
where `´\mathit{n}´` is the ordinal number of the case in the companion object, starting from 0.
1007+
**NOTE:** It is an error if a class case refers to a type parameter of `´E´` in a parameter type in `<type-params>` or `<value-params>` or in a type argument of `<parents>`, unless that parameter is already a type parameter of the case, i.e. the parameter name is defined in `<type-params>`.
1008+
1009+
###### Superclass of an enum case
1010+
1011+
an enum case (singleton or class) with explicit extends clause
1012+
```scala
1013+
case ´C´ <type-params> <value-params> extends <parents>
1014+
```
1015+
1016+
must extend the parent enum `´E´` as the first parent of `<parents>`.
1017+
1018+
###### Example
1019+
Consider the enumeration `RGB`, consisting of simple enum cases:
1020+
```scala
1021+
enum RGB:
1022+
case Red, Green, Blue
1023+
```
1024+
1025+
The three simple cases will expand as follows in the companion of `RGB`:
1026+
1027+
```scala
1028+
val Red = $new(0, "Red")
1029+
val Green = $new(1, "Green")
1030+
val Blue = $new(2, "Blue")
1031+
1032+
private def $new(_$ordinal: Int, $name: String) =
1033+
new RGB with scala.runtime.EnumValue:
1034+
def ordinal = _$ordinal
1035+
override def productPrefix = $name
1036+
override def toString = $name
1037+
```
1038+
1039+
1040+
###### Example
1041+
1042+
Consider the more complex enumeration `Color`, consisting of value enum cases:
1043+
```scala
1044+
enum Color(val rgb: Int):
1045+
case Red extends Color(0xFF0000)
1046+
case Green extends Color(0x00FF00)
1047+
case Blue extends Color(0x0000FF)
1048+
```
1049+
1050+
The three value cases will expand as follows in the companion of `Color`:
1051+
1052+
```scala
1053+
val Red = new Color(0xFF0000):
1054+
def ordinal: Int = 0
1055+
override def productPrefix: String = "Red"
1056+
override def toString: String = "Red"
1057+
val Green = new Color(0x00FF00):
1058+
def ordinal: Int = 1
1059+
override def productPrefix: String = "Green"
1060+
override def toString: String = "Green"
1061+
val Blue = new Color(0x0000FF):
1062+
def ordinal: Int = 2
1063+
override def productPrefix: String = "Blue"
1064+
override def toString: String = "Blue"
1065+
```
1066+
1067+
### Widening of enum cases post-construction
1068+
The compiler-generated `apply` and `copy` methods of an class enum case
1069+
```scala
1070+
case ´C´[´\mathit{tps}\,´](´\mathit{ps}_1\,´)...(´\mathit{ps}_n´) extends ´P_1´, ..., ´P_n´
1071+
```
1072+
are treated specially.
1073+
A call `´C´[´\mathit{tps}\,´](´\mathit{ps}_1\,´)...(´\mathit{ps}_n´)` of the `apply` method is ascribed the underlying type `´P_1´ & ... & ´P_n´` (dropping any [transparent traits](../other-new-features/transparent-traits.md)) as long as that type is still compatible with the expected type at the point of application.
1074+
A call `t.copy[´\mathit{tps}\,´](´\mathit{ps}_1\,´)...(´\mathit{ps}_n´)` of `´C´`'s `copy` method is treated in the same way.
1075+
1076+
### Translation of enums with only singleton cases
1077+
1078+
An enum `´E´` (possibly generic) that defines one or more singleton cases, and no class cases will define the following additional synthetic members in its companion object (where `´E'´` denotes `´E´` with any type parameters replaced by wildcards):
1079+
1080+
- A method `valueOf(name: String): ´E'´`.
1081+
It returns the singleton case value whose identifier is `name`.
1082+
- A method `values` which returns an `Array[´E'´]` of all singleton case values defined by `E`, in the order of their definitions.
1083+
1084+
### Factory method for simple enum cases
1085+
1086+
If an enum `´E´` contains at least one simple case, its companion object will define in addition:
1087+
1088+
- A private method `$new` which defines a new simple case value with given ordinal number and name.
1089+
This method can be thought as being defined as follows.
1090+
1091+
```scala
1092+
private def $new(_$ordinal: Int, $name: String): ´E´ with runtime.EnumValue
1093+
```
1094+
- `$new` returns a new instance of an anonymous class which implements the abstract `Product` methods that it inherits from `Enum`.
1095+
- if `´E´` inherits from `java.lang.Enum` the anonymous class does not override the `ordinal` or `toString` methods, as these are final in `java.lang.Enum`.
1096+
Additionally `productPrefix` will delegate to `this.name`.
1097+
1098+
### Translation of Java-compatible enums
1099+
1100+
A Java-compatible enum is an enum that extends `java.lang.Enum`.
1101+
The translation rules are the same as above, with the reservations defined in this section.
1102+
1103+
- It is a compile-time error for a Java-compatible enum to have class cases.
1104+
1105+
- Cases such as `case C` expand to a `@static val` as opposed to a `val`.
1106+
This allows them to be generated as static fields of the enum type, thus ensuring they are represented the same way as Java enums.
1107+
1108+
### Scopes for Enum Cases
1109+
1110+
A case in an `enum` is treated similarly to a secondary constructor.
1111+
It can access neither the enclosing `enum` using `this`, nor its value parameters or instance members using simple identifiers.
1112+
1113+
Even though translated enum cases are located in the enum's companion object, referencing this object or its members via `this` or a simple identifier is also illegal.
1114+
The compiler typechecks enum cases in the scope of the enclosing companion object but flags any such illegal accesses as errors.
1115+
1116+
### Variance for Type Parameters
1117+
1118+
A parameterized enum case ´C´ of enum ´E´ with _inferred_ type parameters will copy variance annotations.
1119+
e.g. type parameter ´T_{i}´ from ´E´ will have the same variance as type parameter `´T'_{i}´` in ´C´.
1120+
1121+
###### Example
1122+
1123+
The following enum `View` has a contravariant type parameter ´T´ and a single case `Refl`, representing a function mapping a type `T` to itself:
1124+
1125+
```scala
1126+
enum View[-´T´]:
1127+
case Refl(f: ´T´ => ´T´)
1128+
```
1129+
1130+
`Refl` expands to the following enum:
1131+
1132+
```scala
1133+
enum View[-´T´]:
1134+
case Refl[-´T'´](f: ´T'´ => ´T'´) extends View[´T'´]
1135+
```
1136+
1137+
The definition of `Refl` is incorrectly typed, as it uses contravariant type `´T'´` in the covariant result position of a function type.
1138+
1139+
A correctly typed version would use an _explicit_, _invariant_ type parameter `´R´` on case `Refl`:
1140+
1141+
```scala
1142+
enum View[-´T´]:
1143+
case ReflR´](f: ´R´ => ´R´) extends ViewR´]
1144+
```

docs/_spec/12-the-scala-standard-library.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,10 @@ class PartialFunction[-A, +B] extends Function1[A, B] {
345345
<!-- TODO: Could not find more info on which non-Product methods case class automatically define -->
346346
All case classes automatically extend the `Product` trait (and generate synthetic methods to conform to it) (but not `Product´n´`), and define a `_´n´` method for each of their arguments.
347347

348+
### Trait `Enum`
349+
<!-- TODO: Move somewhere else ? -->
350+
All enum definitions automatically extend the `reflect.Enum` trait (and generate synthetic methods to conform to it).
351+
348352
### Class `Array`
349353

350354
All operations on arrays desugar to the corresponding operations of the underlying platform.

0 commit comments

Comments
 (0)