Add Block Results
parent
1c2af0bbe4
commit
bb67aec303
102
Block-Results.md
Normal file
102
Block-Results.md
Normal file
@ -0,0 +1,102 @@
|
||||
In Rust, a block that ends with a semicolon evaluates to `()`, and a block that doesn't end with a semicolon evaluates to the value of its last expression. In Husht, we want semicolons to be unnecessary, so how do we make this distinction? Here are some ideas.
|
||||
|
||||
### Explicitly mark results
|
||||
|
||||
To indicate that a block evaluates to its last expression, you mark the last expression with a special token. This inverts the Rust convention, in the sense that you indicate a block result by the presence of a token rather than the absence of one.
|
||||
|
||||
Pros
|
||||
|
||||
- Simple rule
|
||||
- Doesn't require type-checking in the transpiler
|
||||
- Tight correspondence with Rust syntax
|
||||
|
||||
Cons
|
||||
|
||||
- Adds a little noise—but block results usually make up a small fraction of expressions, so noise should still decrease overall
|
||||
- Prevents some Rust code from being valid Husht
|
||||
|
||||
Here are some ideas for result markers.
|
||||
|
||||
#### Arrow away from result
|
||||
|
||||
"Ship out." For function blocks, can be seen as the input end of the return type arrow in the signature.
|
||||
|
||||
```
|
||||
let q = x*x - y*y
|
||||
if q >= 0
|
||||
<- x
|
||||
else
|
||||
<- y
|
||||
```
|
||||
|
||||
#### Arrow toward result
|
||||
|
||||
"Out of the pipe." For function blocks, can be seen as standing in for the return type in the signature.
|
||||
|
||||
```
|
||||
let q = x*x - y*y
|
||||
if q >= 0
|
||||
-> x
|
||||
else
|
||||
-> y
|
||||
```
|
||||
|
||||
#### Dollar sign
|
||||
|
||||
"Cash out."
|
||||
|
||||
```
|
||||
let q = x*x - y*y
|
||||
if q >= 0
|
||||
$ x
|
||||
else
|
||||
$ y
|
||||
```
|
||||
|
||||
### Allow implicit coercion to `Unit`
|
||||
|
||||
There are few or no circumstances where Rust allows implicit coercion to `Unit`, but Husht could be more permissive about this—perhaps only in particular [coercion sites](https://doc.rust-lang.org/reference/type-coercions.html). This might allow us to infer most of the time whether a block should be treated as though the last expression ends with a semicolon. However, it would probably also lead to ambiguity in weird but not totally unreasonable code like the following, where we could infer either `i32` or `Unit` for the type of `result`.
|
||||
|
||||
```
|
||||
fn say_hello
|
||||
println! "Hello"
|
||||
0
|
||||
|
||||
fn say_goodbye
|
||||
println! "Goodbye"
|
||||
1
|
||||
|
||||
fn say_something(arriving: bool)
|
||||
let result = if arriving
|
||||
say_hello()
|
||||
else
|
||||
say_goodbye()
|
||||
/* function body continues... */
|
||||
```
|
||||
|
||||
Ideas for resolving the ambiguity:
|
||||
|
||||
- Only do implicit coercion as a "last resort" (no idea how to formalize that)?
|
||||
- Storing or using the result of a block forces it to take the value of its final expressions?
|
||||
- Require explicit coercion in cases like this?
|
||||
|
||||
Pros
|
||||
|
||||
- Might reduce verbosity in many different situations.
|
||||
|
||||
Cons
|
||||
|
||||
- Requires type-checking in the transpiler
|
||||
- Messing with such a basic feature of Rust could have far-reaching effects with hard-to-foresee consequences
|
||||
- I'm worried that this could collapse lots of different type errors into errors involving `()`, and obscure many errors' locations. That can lead to frustrating, uninformative error messages, akin to "... ended by `\end{document}` in LaTeX. For example, the Rust code
|
||||
|
||||
```
|
||||
let x = if true {
|
||||
2.0
|
||||
} else {
|
||||
3
|
||||
};
|
||||
let x_sq = x*x;
|
||||
```
|
||||
|
||||
gives the error "`if` and `else` have incompatible types" error. Allowing implicit coercion to `()` might mean we instead get the error "cannot multiply `()` by `()`" in the expression `x*x`, making it harder to figure out what and where the problem is.
|
Loading…
Reference in New Issue
Block a user