I've been working for a while now on making it possible to use the
syntactic sugar for blocks within expressions. I've been doing various
iterations, trying to find the formulation that was both (a) easy enough
to explain and (b) made minimal changes to the existing language syntax
/ code. Although I think my changes only affect corner cases, they are
not backwards compatible, so I wanted to run the proposal by the list
before pushing it to master.
First, the goal of the change is to allow expressions like:
let v = vec::map(v) { |e| ... };
Currently, these must be written:
let v = vec::map(v, { |e| ... });
The syntactic sugar which allows the final block argument to appear
outside the parentheses is only currently permitted for top-level
statements. Furthermore, the result of such a sugared statement is
*always* ignored. So something like this would parse but not type check:
fn foo(v: [T]) -> bool {
vec::any(v) { |e| test_some_condition(e) }
}
Here the `vec::any(v) {||}` call would not be interpreted as the result
of the block but rather as a call whose result should be discarded,
similar to how a `while` loop would be interpreted.
To fix I made the following changes:
1. Dual-mode expressions like `if`, `while`, `do-while`, blocks, and of
course sugared calls cannot be followed by binary operators or further
calls when they appear at the top level (i.e., not nested within some
other statement or expression).
2. All blocks may have tail expressions. The type checker will
guarantee that the tail expressions have unit type when they appear in
the body of a while loop or other similar context. This used to be
enforced by the parser.
Across the entire compiler, two changes were required as a result of
these rules. As it happens, one change was required by rule #1 and one
by rule #2, so they also serve as a good way to explain the rules.
## Change #1: combining a dual-mode expression with a binary operator ##
This function
fn companion_file(prefix: str, suffix: option::t<str>) -> str {
alt suffix {
option::some(s) { fs::connect(prefix, s) }
option::none. { prefix }
} + ".rs"
}
no longer parses. This is because the `alt suffix {...}` is parsed as a
statement because it begins the statement. Therefore, the `+".rs"` is
parsed as an expression, and `+` is not a legal unary operator. This
function had to be changed as follows:
fn companion_file(prefix: str, suffix: option::t<str>) -> str {
ret alt suffix {
option::some(s) { fs::connect(prefix, s) }
option::none. { prefix }
} + ".rs";
}
Here I used a `ret` to place the `alt` into expression position. You
could also have used parentheses:
fn companion_file(prefix: str, suffix: option::t<str>) -> str {
(alt suffix {
option::some(s) { fs::connect(prefix, s) }
option::none. { prefix }
} + ".rs")
}
## Change #2: ignoring the result value ##
The following function from rope.rs no longer type checks:
fn iter_chars(rope: rope, it: block(char)) {
loop_chars(rope) {|x|
it(x);
true
}
}
The type check fails because the `loop_chars()` function returns a
boolean value. The call appears in the tail position of the function
`iter_chars()` which has a unit return type. So there is a type error
"expected `()` but found`boolean`". The fix is simply to introduce an
explicit semicolon to ignore the result:
fn iter_chars(rope: rope, it: block(char)) {
loop_chars(rope) {|x|
it(x);
true
};
}
## More details ##
If you want more details, I wrote up the various options I considered
here:
<http://smallcultfollowing.com/babysteps/blog/2011/12/29/block-sugar-in-expressions/>
Ok, that's it, thoughts?
Niko
_______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev