From bb67aec30372d765cb394e035383bace4ac1c4fe Mon Sep 17 00:00:00 2001 From: Glen Whitney Date: Fri, 30 Aug 2024 22:23:09 +0000 Subject: [PATCH] Add Block Results --- Block-Results.md | 102 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 Block-Results.md diff --git a/Block-Results.md b/Block-Results.md new file mode 100644 index 0000000..a2c3497 --- /dev/null +++ b/Block-Results.md @@ -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. \ No newline at end of file