-
-
Notifications
You must be signed in to change notification settings - Fork 69
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add guard
and guard.let
#637
base: master
Are you sure you want to change the base?
Conversation
|
||
|
||
defn.sequence_macro 'guard $condition ... | $failure_body | ||
$success_body': |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does $success_body
need a ...
, or does defn.sequence_macro
do something where a plain trailing $
-binding implicitly matches the remainder of input?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The latter. $success_body
here matches the remainder of the enclosing block.
I really like the look of this (especially as someone who's used Rust and Swift a fair bit). It also hadn't even occurred to me to use One thing I'm still slightly wrestling with is that it does use the purely syntactic notion of a defn-or-expr sequence for what constitutes the code to short-circuit. This is almost certainly for the best, since it keeps it easy to understand how |
One thing that would be really nice would be to have a way of producing a failure result automatically based on static information for the enclosing context, and then having |
This is a really nice idea!
Ah, that would be great. I get hung up on the syntax of the examples with this extra "guard" stuff. But if there was a specific sentinel value that these bindings worked with somehow and you could write patterns that explicitly didn't include that sentinel and then that would mean "abort to the enclosing block" but doing it all statically, not via (escaping) continuations, I think that would make the notation look very nice. And DrRacket/Emacs/VSCode could offer you some kind of feedback when you've done that so you could easily see the shortcircuiting potential. |
I like the idea of having a syntactically-lightweight error-handling mechanism that "synthesizes" the aborting path, but my feeling is that that makes more sense as a separate mechanism / construct. Though I might just be biased by analogizing to Rust/Swift/Zig (where constructs like I also think there may be a question there of "how far to escape". In Rust/Swift/Zig, the |
Unless I misunderstand, this doesn't sound like an approach that would work well for Rhombus. Things that look like nested-block forms may actually be implemented with Along the lines of functional-versus-imperative style, I think I wouldn't want to include a A block-based |
I hadn't considered implicit introduction of FWIW, limited forms of |
Something like |
This pull request adds guard statements (as seen in Swift and Rust) in the form of two new control flow utility macros,
guard
andguard.let
. They're definition sequence macros that give straight-line code the ability to escape early if a condition isn't met. Their semantics are the same as in myguard
package for Racket, with the exception thatdefine/guard
andguarded-block
aren't needed. As an example, here are two versions of alist_equals(xs, ys)
function, one with guard statements and one without:The
guard
form checks that an expression is true; otherwise, it short-circuits the enclosing block and evaluates the given alternative. Theguard.let
form checks that a pattern matches an expression or else short-circuits. They're equivalent to putting the remainder of the block in an enclosingif
or amatch
expression. Here is a longer piece of example code that uses a mix ofguard
andguard.let
statements:Tests and documentation are not yet included, as I wanted to get feedback on the proposal first.