Skip to content

Reference sections for some of the new things in Dotty #2498

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

Merged
merged 25 commits into from
May 26, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -431,11 +431,18 @@ object desugar {
// For all other classes, the parent is AnyRef.
val companions =
if (isCaseClass) {
def extractType(t: Tree): Tree = t match {
case Apply(t1, _) => extractType(t1)
case TypeApply(t1, ts) => AppliedTypeTree(extractType(t1), ts)
case Select(t1, nme.CONSTRUCTOR) => extractType(t1)
case New(t1) => t1
case t1 => t1
}
// The return type of the `apply` method
val applyResultTpt =
if (isEnumCase)
if (parents.isEmpty) enumClassTypeRef
else parents.reduceLeft(AndTypeTree)
else parents.map(extractType).reduceLeft(AndTypeTree)
else TypeTree()

val parent =
Expand Down
10 changes: 8 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -443,13 +443,19 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
tree.tpt match {
case templ: untpd.Template =>
import untpd._
templ.parents foreach {
var templ1 = templ
def isEligible(tp: Type) = tp.exists && !tp.typeSymbol.is(Final)
if (templ1.parents.isEmpty &&
isFullyDefined(pt, ForceDegree.noBottom) &&
isEligible(pt.underlyingClassRef(refinementOK = false)))
templ1 = cpy.Template(templ)(parents = untpd.TypeTree(pt) :: Nil)
templ1.parents foreach {
case parent: RefTree =>
typedAheadImpl(parent, tree => inferTypeParams(typedType(tree), pt))
case _ =>
}
val x = tpnme.ANON_CLASS
val clsDef = TypeDef(x, templ).withFlags(Final)
val clsDef = TypeDef(x, templ1).withFlags(Final)
typed(cpy.Block(tree)(clsDef :: Nil, New(Ident(x), Nil)), pt)
case _ =>
var tpt1 = typedType(tree.tpt)
Expand Down
8 changes: 8 additions & 0 deletions docs/docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ the code, setup Dotty with your favorite IDE and more!
Contents
-------

* Dotty Language Reference
- [Intersection Types](reference/intersection-types.md)
- [Union Types](reference/union-types.md)
- [Trait Parameters](reference/trait-parameters.md)
- [Enumerations](reference/enums.md)
- [Algebraic Data Types](reference/adts.md)
- [Enum Translation](reference/desugarEnums.md)
- [By-Name Implicits](reference/implicit-by-name-parameters.md)
* Usage
- [Migrating from Scala 2](usage/migrating.md): migration information
- [Dotty projects with cbt](usage/cbt-projects.md): using cbt
Expand Down
122 changes: 122 additions & 0 deletions docs/docs/reference/adts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
---
layout: doc-page
title: "Algebraic Data Types"
---


The `enum` concept is general enough to also support algebraic data
types (ADTs) and their generalized version (GADTs). Here's an example
how an `Option` type can be represented as an ADT:

```scala
enum Option[+T] {
case Some[+T](x: T)
case None
}
```

This example introduces `Option` enum class with a covariant type
parameter `T`, together with two cases, `Some` and `None`. `Some` is
parameterized with a type parameter `T` and a value parameter `x`. It
is a shorthand for writing a case class that extends `Option`. Since
`None` is not parameterized it is treated as a normal enum value.

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

```scala
enum Option[+T] {
case Some[+T](x: T) extends Option[T]
case None extends Option[Nothing]
}
```

Note that the parent type of `None` is inferred as
`Option[Nothing]`. Generally, all covariant type parameters of the enum
class are minimized in a compiler-generated extends clause whereas all
contravariant type parameters are maximized. If `Option` was non-variant,
you'd need to give the extends clause of `None` explicitly.

As for normal enum values, the cases of an `enum` are all defined in
the `enum`s companion object. So it's `Option.Some` and `Option.None`
unless the definitions are "pulled out" with an import:

```scala
scala> Option.Some("hello")
val res1: t2.Option[String] = Some(hello)
scala> Option.None
val res2: t2.Option[Nothing] = None
```

Note that the type of the expressions above is always `Option`. That
is, the implementation case classes are not visible in the result
types of their `apply` methods. This is a subtle difference with
respect to normal case classes. The classes making up the cases do
exist, and can be unvealed by constructing them directly with a `new`.

```scala
val res3: t2.Option.Some[Int] = Some(2)
scala> scala> new Option.Some(2)
```

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

```scala
enum class Option[+T] {
def isDefined: Boolean
}
object Option {
def apply[T >: Null](x: T): Option[T] =
if (x == null) None else Some(x)

case Some[+T](x: T) {
def isDefined = true
}
case None {
def isDefined = false
}
}
```

Enumerations and ADTs have been presented as two different
concepts. But since they share the same syntactic construct, they can
be seen simply as two ends of a spectrum and it is perfectly possible
to conctruct hybrids. For instance, the code below gives an
implementation of `Color` either with three enum values or with a
parameterized case that takes an RGB value.

```scala
enum Color(val rgb: Int) {
case Red extends Color(0xFF0000)
case Green extends Color(0x00FF00)
case Blue extends Color(0x0000FF)
case Mix(mix: Int) extends Color(mix)
}
```

## Syntax of Enums

Changes to the syntax fall in two categories: enum classes and cases inside enums.
The changes are specified below as deltas with respect to the Scala syntax given [here](https://github.com/lampepfl/dotty/blob/master/docs/docs/internals/syntax.md)

1. Enum definitions and enum classes are defined as follows:

TmplDef ::= `enum' `class’ ClassDef
| `enum' EnumDef
EnumDef ::= id ClassConstr [`extends' [ConstrApps]]
[nl] `{’ EnumCaseStat {semi EnumCaseStat} `}’

2. Cases of enums are defined as follows:

EnumCaseStat ::= {Annotation [nl]} {Modifier} EnumCase
EnumCase ::= `case' (EnumClassDef | ObjectDef | ids)
EnumClassDef ::= id [ClsTpeParamClause | ClsParamClause]
ClsParamClauses TemplateOpt
TemplateStat ::= ... | EnumCaseStat
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be TemplateOpt or the above line be TemplateStat






165 changes: 165 additions & 0 deletions docs/docs/reference/desugarEnums.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
---
layout: doc-page
title: "Translation of Enums and ADTs"
---

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

We now explain the expansion of enums is explained in detail. First,
some terminology and notational conventions:

- 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`.
- 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.

- Enum cases fall into three categories:

- _Class cases_ are those cases that are parameterized, either with a type parameter section `[...]` or with one or more (possibly empty) parameter sections `(...)`.
- _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.
- _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.

Simple cases and value cases are called collectively _singleton cases_.

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

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

1. An `enum` definition

enum E ... { <cases> }

expands to an enum class and a companion object

enum class E ...
object E { <cases> }

2. An enum class definition

enum class E ... extends <parents> ...

expands to a `sealed` `abstract` class that extends the `scala.Enum` trait:

sealed abstract class E ... extends <parents> with scala.Enum ...

3. If `E` is an enum class without type parameters, then a case in its companion object without an extends clause

case C <params> <body>

expands to

case C <params> <body> extends E

4. If `E` is an enum class with type parameters `Ts`, then a case in its
companion object without an extends clause

case C <params> <body>

expands according to two alternatives, depending whether `C` has type
parameters or not. If `C` has type parameters, they must have the same
names and appear in the same order as the enum type parameters `Ts`
(variances may be different, however). In this case

case C [Ts] <params> <body>

expands to

case C[Ts] <params> extends E[Ts] <body>

For the case where `C` does not have type parameters, assume `E`'s type
parameters are

V1 T1 > L1 <: U1 , ... , Vn Tn >: Ln <: Un (n > 0)

where each of the variances `Vi` is either `'+'` or `'-'`. Then the case
expands to

case C <params> extends E[B1, ..., Bn] <body>

where `Bi` is `Li` if `Vi = '+'` and `Ui` if `Vi = '-'`. It is an error if
`Bi` refers to some other type parameter `Tj (j = 0,..,n-1)`. It is also
an error if `E` has type parameters that are non-variant.

5. A class case

case C <params> ...

expands analogous to a case class:

final case class C <params> ...

However, unlike for a regular case class, the return type of the associated
`apply` method is a fully parameterized type instance of the enum class `E`
itself instead of `C`. Also the enum case defines an `enumTag` method of
the form

def enumTag = n

where `n` is the ordinal number of the case in the companion object,
starting from 0.

6. A value case

case C extends <parents> <body>

expands to a value definition

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

where `n` is the ordinal number of the case in the companion object,
starting from 0. The statement `$values.register(this)` registers the value
as one of the `enumValues` of the enumeration (see below). `$values` is a
compiler-defined private value in the companion object.

7. A simple case

case C

of an enum class `E` that does not take type parameters expands to

val C = $new(n, "C")

Here, `$new` is a private method that creates an instance of of `E` (see
below).

8. A simple case consisting of a comma-separated list of enum names

case C_1, ..., C_n

expands to

case C_1; ...; case C_n

Any modifiers or annotations on the original case extend to all expanded
cases.

## Translation of Enumerations

Non-generic enum classes `E` that define one or more singleton cases
are called _enumerations_. Companion objects of enumerations define
the following additional members.

- A method `enumValue` of type `scala.collection.immutable.Map[Int, E]`.
`enumValue(n)` returns the singleton case value with ordinal number `n`.
- A method `enumValueNamed` of type `scala.collection.immutable.Map[String, E]`.
`enumValueNamed(s)` returns the singleton case value whose `toString`
representation is `s`.
- A method `enumValues` which returns an `Iterable[E]` of all singleton case
values in `E`, in the order of their definitions.

Companion objects that contain at least one simple case define in addition:

- A private method `$new` which defines a new simple case value with given
ordinal number and name. This method can be thought as being defined as
follows.

def $new(tag: Int, name: String): ET = new E {
def enumTag = tag
def toString = name
$values.register(this) // register enum value so that `valueOf` and `values` can return it.
}
Loading