Skip to content

Commit 912c672

Browse files
committed
Update docs
- Update current reference docs to what's implemented in 0.19. - Add links to new pages describing what will come in 0.20.
1 parent 3463d17 commit 912c672

File tree

5 files changed

+346
-38
lines changed

5 files changed

+346
-38
lines changed
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
---
2+
layout: doc-page
3+
title: New Control Syntax
4+
---
5+
6+
Scala 3 has a new "quiet" syntax for control expressions that does not rely in
7+
enclosing the condition in parentheses, and also allows to drop parentheses or braces
8+
around the generators of a `for`-expression. Examples:
9+
```scala
10+
if x < 0
11+
"negative"
12+
else if x == 0
13+
"zero"
14+
else
15+
"positive"
16+
17+
if x < 0 then -x else x
18+
19+
while x >= 0 do x = f(x)
20+
21+
for x <- xs if x > 0
22+
yield x * x
23+
24+
for
25+
x <- xs
26+
y <- ys
27+
do
28+
println(x + y)
29+
```
30+
31+
The rules in detail are:
32+
33+
- The condition of an `if`-expression can be written without enclosing parentheses if it is followed by a `then`
34+
or some [indented](./indentation.html) code on a following line.
35+
- The condition of a `while`-loop can be written without enclosing parentheses if it is followed by a `do`.
36+
- The enumerators of a `for`-expression can be written without enclosing parentheses or braces if they are followed by a `yield` or `do`.
37+
- A `do` in a `for`-expression expresses a `for`-loop.
38+
39+
40+
### Rewrites
41+
42+
The Dotty compiler can rewrite source code from old syntax and new syntax and back.
43+
When invoked with options `-rewrite -new-syntax` it will rewrite from old to new syntax, dropping parentheses and braces in conditions and enumerators. When invoked with with options `-rewrite -old-syntax` it will rewrite in the reverse direction, inserting parentheses and braces as needed.

docs/docs/reference/other-new-features/control-syntax.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ layout: doc-page
33
title: New Control Syntax
44
---
55

6+
**Note** The syntax described in this section is currently under revision.
7+
[Here is the new version which will be implemented in Dotty 0.20](./control-syntax-new.html).
8+
69
Scala 3 has a new "quiet" syntax for control expressions that does not rely in
710
enclosing the condition in parentheses, and also allows to drop parentheses or braces
811
around the generators of a `for`-expression. Examples:
Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
---
2+
layout: doc-page
3+
title: Optional Braces
4+
---
5+
6+
As an experimental feature, Scala 3 treats indentation as significant and allows
7+
some occurrences of braces `{...}` to be optional.
8+
9+
- First, some badly indented programs are ruled out, which means they are flagged with warnings.
10+
- Second, some occurrences of braces `{...}` are made optional. Generally, the rule
11+
is that adding a pair of optional braces will not change the meaning of a well-indented program.
12+
13+
### Indentation Rules
14+
15+
The compiler enforces three rules for well-indented programs, flagging violations as warnings.
16+
17+
1. In a brace-delimited region, no statement is allowed to start to the left
18+
of the first statement after the opening brace that starts a new line.
19+
20+
This rule is helpful for finding missing closing braces. It prevents errors like:
21+
22+
```scala
23+
if (x < 0) {
24+
println(1)
25+
println(2)
26+
27+
println("done") // error: indented too far to the left
28+
```
29+
30+
2. If significant indentation is turned off (i.e. under Scala-2 mode or under `-noindent`) and we are at the start of an indented sub-part of an expression, and the indented part ends in a newline, the next statement must start at an indentation width less than the sub-part. This prevents errors where an opening brace was forgotten, as in
31+
32+
```scala
33+
if (x < 0)
34+
println(1)
35+
println(2) // error: missing `{`
36+
```
37+
38+
3. If significant indentation is turned off, code that follows a class or object definition (or similar) lacking a `{...}` body may not be indented more than that definition. This prevents misleading situations like:
39+
40+
```scala
41+
trait A
42+
case class B() extends A // error: indented too far to the right
43+
```
44+
It requires that the case class `C` to be written instead at the same level of indentation as the trait `A`.
45+
46+
These rules still leave a lot of leeway how programs should be indented. For instance, they do not impose
47+
any restrictions on indentation within expressions, nor do they require that all statements of an indentation block line up exactly.
48+
49+
The rules are generally helpful in pinpointing the root cause of errors related to missing opening or closing braces. These errors are often quite hard to diagnose, in particular in large programs.
50+
51+
### Optional Braces
52+
53+
The compiler will insert `<indent>` or `<outdent>`
54+
tokens at certain line breaks. Grammatically, pairs of `<indent>` and `<outdent>` tokens have the same effect as pairs of braces `{` and `}`.
55+
56+
The algorithm makes use of a stack `IW` of previously encountered indentation widths. The stack initially holds a single element with a zero indentation width. The _current indentation width_ is the indentation width of the top of the stack.
57+
58+
There are two rules:
59+
60+
1. An `<indent>` is inserted at a line break, if
61+
62+
- An indentation region can start at the current position in the source, and
63+
- the first token on the next line has an indentation width strictly greater
64+
than the current indentation width
65+
66+
An indentation region can start
67+
68+
- after the condition of an `if-else`, or
69+
- at points where a set of definitions enclosed in braces is expected in a
70+
class, object, given, or enum definition, in an enum case, or after a package clause, or
71+
- after one of the following tokens:
72+
```
73+
= => <- if then else while do try catch finally for yield match
74+
```
75+
If an `<indent>` is inserted, the indentation width of the token on the next line
76+
is pushed onto `IW`, which makes it the new current indentation width.
77+
78+
2. An `<outdent>` is inserted at a line break, if
79+
80+
- the first token on the next line has an indentation width strictly less
81+
than the current indentation width, and
82+
- the first token on the next line is not a
83+
[leading infix operator](../changed-features/operators.html).
84+
85+
If an `<outdent>` is inserted, the top element if popped from `IW`.
86+
If the indentation width of the token on the next line is still less than the new current indentation width, step (2) repeats. Therefore, several `<outdent>` tokens
87+
may be inserted in a row.
88+
89+
It is an error if the indentation width of the token following an `<outdent>` does not
90+
match the indentation of some previous line in the enclosing indentation region. For instance, the following would be rejected.
91+
```scala
92+
if x < 0
93+
-x
94+
else // error: `else` does not align correctly
95+
x
96+
```
97+
Indentation tokens are only inserted in regions where newline statement separators are also inferred:
98+
at the toplevel, inside braces `{...}`, but not inside parentheses `(...)`, patterns or types.
99+
100+
### Spaces vs Tabs
101+
102+
Indentation prefixes can consist of spaces and/or tabs. Indentation widths are the indentation prefixes themselves, ordered by the string prefix relation. So, so for instance "2 tabs, followed by 4 spaces" is strictly less than "2 tabs, followed by 5 spaces", but "2 tabs, followed by 4 spaces" is incomparable to "6 tabs" or to "4 spaces, followed by 2 tabs". It is an error if the indentation width of some line is incomparable with the indentation width of the region that's current at that point. To avoid such errors, it is a good idea not to mix spaces and tabs in the same source file.
103+
104+
### Indentation and Braces
105+
106+
Indentation can be mixed freely with braces. For interpreting indentation inside braces, the following rules apply.
107+
108+
1. The assumed indentation width of a multiline region enclosed in braces is the
109+
indentation width of the first token that starts a new line after the opening brace.
110+
111+
2. On encountering a closing brace `}`, as many `<outdent>` tokens as necessary are
112+
inserted to close all open indentation regions inside the pair of braces.
113+
114+
### Special Treatment of Case Clauses
115+
116+
The indentation rules for `match` expressions and `catch` clauses are refined as follows:
117+
118+
- An indentation region is opened after a `match` or `catch` also if the following `case`
119+
appears at the indentation width that's current for the `match` itself.
120+
- In that case, the indentation region closes at the first token at that
121+
same indentation width that is not a `case`, or at any token with a smaller
122+
indentation width, whichever comes first.
123+
124+
The rules allow to write `match` expressions where cases are not indented themselves, as in the example below:
125+
```scala
126+
x match
127+
case 1 => print("I")
128+
case 2 => print("II")
129+
case 3 => print("III")
130+
case 4 => print("IV")
131+
case 5 => print("V")
132+
133+
println(".")
134+
```
135+
136+
### The End Marker
137+
138+
Indentation-based syntax has many advantages over other conventions. But one possible problem is that it makes it hard to discern when a large indentation region ends, since there is no specific token that delineates the end. Braces are not much better since a brace by itself also contains no information about what region is closed.
139+
140+
To solve this problem, Scala 3 offers an optional `end` marker. Example
141+
```scala
142+
def largeMethod(...) =
143+
...
144+
if ... then ...
145+
else
146+
... // a large block
147+
end if
148+
... // more code
149+
end largeMethod
150+
```
151+
An `end` marker consists of the identifier `end` which follows an `<outdent>` token, and is in turn followed on the same line by exactly one other token, which is either an identifier or one of the reserved words
152+
```scala
153+
if while for match try new
154+
```
155+
If `end` is followed by a reserved word, the compiler checks that the marker closes an indentation region belonging to a construct that starts with the reserved word. If it is followed by an identifier _id_, the compiler checks that the marker closes a definition
156+
that defines _id_ or a package clause that refers to _id_.
157+
158+
`end` itself is a soft keyword. It is only treated as an `end` marker if it
159+
occurs at the start of a line and is followed by an identifier or one of the reserved words above.
160+
161+
It is recommended that `end` markers are used for code where the extent of an indentation region is not immediately apparent "at a glance". Typically this is the case if an indentation region spans 20 lines or more.
162+
163+
### Example
164+
165+
Here is a (somewhat meta-circular) example of code using indentation. It provides a concrete representation of indentation widths as defined above together with efficient operations for constructing and comparing indentation widths.
166+
167+
```scala
168+
enum IndentWidth
169+
case Run(ch: Char, n: Int)
170+
case Conc(l: IndentWidth, r: Run)
171+
172+
def <= (that: IndentWidth): Boolean =
173+
this match
174+
case Run(ch1, n1) =>
175+
that match
176+
case Run(ch2, n2) => n1 <= n2 && (ch1 == ch2 || n1 == 0)
177+
case Conc(l, r) => this <= l
178+
case Conc(l1, r1) =>
179+
that match
180+
case Conc(l2, r2) => l1 == l2 && r1 <= r2
181+
case _ => false
182+
183+
def < (that: IndentWidth): Boolean =
184+
this <= that && !(that <= this)
185+
186+
override def toString: String =
187+
this match
188+
case Run(ch, n) =>
189+
val kind = ch match
190+
case ' ' => "space"
191+
case '\t' => "tab"
192+
case _ => s"'$ch'-character"
193+
val suffix = if n == 1 then "" else "s"
194+
s"$n $kind$suffix"
195+
case Conc(l, r) =>
196+
s"$l, $r"
197+
198+
object IndentWidth
199+
private inline val MaxCached = 40
200+
201+
private val spaces = IArray.tabulate(MaxCached + 1)(new Run(' ', _))
202+
private val tabs = IArray.tabulate(MaxCached + 1)(new Run('\t', _))
203+
204+
def Run(ch: Char, n: Int): Run =
205+
if n <= MaxCached && ch == ' '
206+
spaces(n)
207+
else if n <= MaxCached && ch == '\t'
208+
tabs(n)
209+
else
210+
new Run(ch, n)
211+
end Run
212+
213+
val Zero = Run(' ', 0)
214+
end IndentWidth
215+
```
216+
217+
### Settings and Rewrites
218+
219+
Significant indentation is enabled by default. It can be turned off by giving any of the options `-noindent`, `old-syntax` and `language:Scala2`. If indentation is turned off, it is nevertheless checked that indentation conforms to the logical program structure as defined by braces. If that is not the case, the compiler issues a warning.
220+
221+
The Dotty compiler can rewrite source code to indented code and back.
222+
When invoked with options `-rewrite -indent` it will rewrite braces to
223+
indented regions where possible. When invoked with with options `-rewrite -noindent` it will rewrite in the reverse direction, inserting braces for indentation regions.
224+
The `-indent` option only works on [new-style syntax](./control-syntax.html). So to go from old-style syntax to new-style indented code one has to invoke the compiler twice, first with options `-rewrite -new-syntax`, then again with options
225+
`-rewrite-indent`. To go in the opposite direction, from indented code to old-style syntax, it's `-rewrite -noindent`, followed by `-rewrite -old-syntax`.
226+
227+
### Variant: Indentation Marker `:`
228+
229+
Generally, the possible indentation regions coincide with those regions where braces `{...}` are also legal, no matter whether the braces enclose an expression or a set of definitions. There is one exception, though: Arguments to function can be enclosed in braces but they cannot be simply indented instead. Making indentation always significant for function arguments would be too restrictive and fragile.
230+
231+
To allow such arguments to be written without braces, a variant of the indentation scheme is implemented under
232+
option `-Yindent-colons`. This variant is more contentious and less stable than the rest of the significant indentation scheme. In this variant, a colon `:` at the end of a line is also one of the possible tokens that opens an indentation region. Examples:
233+
234+
```scala
235+
times(10):
236+
println("ah")
237+
println("ha")
238+
```
239+
or
240+
```scala
241+
xs.map:
242+
x =>
243+
val y = x - 1
244+
y * y
245+
```
246+
Colons at the end of lines are their own token, distinct from normal `:`.
247+
The Scala grammar is changed in this variant so that colons at end of lines are accepted at all points
248+
where an opening brace enclosing a function argument is legal. Special provisions are taken so that method result types can still use a colon on the end of a line, followed by the actual type on the next.
249+

0 commit comments

Comments
 (0)