while ... do: inconsistent handling of whitespace / indentation
while ... do expressions handle whitespace / indentation in a manner that is not consistent with if ... then ... else expressions.
This is problematic when the test expression is lengthy and would be more clearly written using multiple lines, along the lines of:
while
x > 10
&&
x < 20
do ...
Repro steps
Provide the steps required to reproduce the problem:
let mutable x = 0
while
x < 10
do
x <- x + 1
Expected behavior
I would expect this to be valid syntax, in a fashion similar to
if
x < 10
then
// do something
else
// do something
Actual behavior
error FS0010: Incomplete structured construct at or before this point in expression
Missing 'do' in 'while' expression. Expected 'while <expr> do <expr>'.
Known workarounds
Any indentation of do resolves the issue:
let mutable x = 0
while
x < 10
do
x <- x + 1
The current behavior is in line with the F# spec.
do is part of the while context and therefore needs to be indented like anything else. (The same is true for do in for contexts.)
For then/else/elif there is an explicit exception to the rule (see §15.1.9 of the F# spec) for good reasons (common case, avoiding multiple indentations).
But I wouldn't introduce any further complexity into the compiler for this case.
This is indeed a language suggestion. However, it is also in line with previous indentation relaxations -
- F# 4.5 permit undentation on [ ... ] and [| ... |]
- F# 4.7 Offside relaxations for construct and member definitions
- F# 6.0 Allow more undentations and remove inconsistencies
- approved in principle https://github.com/fsharp/fslang-suggestions/issues/1111
- https://github.com/fsharp/fslang-suggestions/issues/1433
Definitely agree with @Happypig375 here - there are two 'expression' holes in a while ... do ... construction, and it feels very wrong for the 'holes' not to be able to be indented separately from the control-flow constructs. @mathias-brandewinder asked me about this yesterday, and I was actually stumped - having used this language for more than a decade - about why the behavior would be the way it is.
The main reason I can think of not to do this is syntactic ambiguity between do block in a while ... do ... vs a 'bare' do block - this I think the parser already is capable enough of handling that.
This might also be applicable to for ... = ... to ... do, for ... = ... downto ... do and for ... in ... do loops too.
i.e. a new RFC building upon FS-1108 - Undentation Frenzy with this?
Examples
1. while ... do ...
Currently invalid:
let mutable x = 0
while
x < 10
&& x > -10
do
x <- x + 1
Would become valid.
2. for ... = ... to ... do ...
Currently invalid:
for i =
1
+ 2
to
10
+ 5
do
printfn "%d" i
Would become valid.
3. for ... = ... downto ... do ...
Currently invalid:
for i =
10
* 2
downto
1
+ 1
do
printfn "%d" i
Would become valid.
4. for ... in ... do ...
Currently invalid:
for item in
someVeryLongFunctionNameThatReturnsASequence
param1
param2
param3
do
printfn "%A" item
Would become valid.
@T-Gro Yes.
let mutable x = 0
while
x < 10
&& x > -10
do // should be able to undent here
x <- x + 1
for i =
1
+ 2
to // should be able to undent here
10
+ 5
do // should be able to undent here
printfn "%d" i
for i =
10
* 2
downto // should be able to undent here
1
+ 1
do // should be able to undent here
printfn "%d" i
for
item
in // should be able to undent here
someVeryLongFunctionNameThatReturnsASequence
param1
param2
param3
do // should be able to undent here
printfn "%A" item
In addition to those 4 cases, the 2 suggestions of
- https://github.com/fsharp/fslang-suggestions/issues/1111
- https://github.com/fsharp/fslang-suggestions/issues/1433
should also be considered.