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