Hi racketeers,

I would second this suggestion. Although it might look slightly
un-rackety at first sight, for certain types of code flow it does the
job really well.

And most importantly - I am using escape continuations in much wilder
setup (yes, futures) and it imposes no noticeable performance impact. It
is still on my TODO list to measure that empirically though. On CS that
is - on BC I didn't have time to test it yet.


Dominik

P.S.: Thanks go out to Matthew for pointing ECs out at RacketCon
informal discussions. Futures with generic CC come with absurdly huge
performance penalty even if they stay within the future thread ...

On 28. 10. 20 12:23, Alex Harsanyi wrote:
> Are you looking for `let/ec`?
> 
> (let/ec return
>   (define x (random 10))
>   (unless (even? x)
>     (log-info "x wasn't even, x = ~a" x)
>     (return -1))
>   (define y (random 10))
>   (unless (even? y)
>     (log-info "y wasn't even, y = ~a" y)
>     (return -1))
>   (+ x y))
> 
> Alex.
> 
> On Wednesday, October 28, 2020 at 6:54:44 PM UTC+8 jackh...@gmail.com wrote:
> 
>     So I'm a little tired of writing code like this:
> 
>     (define x ...)
>     (cond
>       [(take-shortcut? x) (shortcut x)]
>       [else
>        (define y (compute-y x))
>        (cond
>         [(take-other-shortcut? x y) (other-shortcut x y)]
>         [else
>          (define z ...)
>          (cond ...)])])
> 
>     That is, I have some logic and that logic occasionally checks for
>     conditions that make the rest of the logic irrelevant, such as an
>     empty or false input or something else that should trigger an early
>     exit. Each check like this requires me to write a |cond| whose
>     |else| clause wraps the remainder of the body, leading to an awkward
>     nesting of |cond| forms. I don't have this issue when the early
>     exits involve raising exceptions: in those cases I can just use
>     |when| and |unless| like so:
> 
>     (define x ...)
>     (unless (passes-check? x) (raise ...))
>     (define y ...)
>     (unless (passes-other-check? x y) (raise ...))
>     (define z ...)
>     ...
> 
>     I'm aware of a few macros in the racket ecosystem that try to solve
>     this problem. For example, Jay wrote a blog post
>     <http://jeapostrophe.github.io/2013-11-12-condd-post.html> that
>     creates a |condd| form that's like |cond| but allows embedded
>     definitions using a |#:do| keyword. I've also seen various
>     approaches that use escape continuations to implement the early
>     exit. There's drawbacks I'm not happy about however:
> 
>       *
> 
>         For |cond|-like macros that allow embedded definitions, it looks
>         too different from regular straight-line Racket code. I like my
>         function bodies to be a sequence of definitions and expressions,
>         with minimal nesting, just like the |when| and |unless| version
>         above. I don't have to use a keyword or extra parentheses to
>         signal whether a form is a definition or a |when| / |unless|
>         check in error-raising code, why should I have to do that in
>         code that uses early returns?
> 
>       *
> 
>         Continuation-based solutions impose a nontrivial performance
>         penalty and have complex semantics. I don't like that the
>         generated code behaves differently from the |cond| tree I would
>         normally write. What happens if I stick an early exit inside a
>         lambda? Or a thread? What if I set up a continuation barrier?
>         Does that matter? I don't know and I don't want to think about
>         that just to write what would be a simple |if (condition) {
>         return ... }| block in other languages.
> 
>     So I wrote a basic macro for this and I have some questions about
>     how to make it more robust. The macro is called |guarded-block| and
>     it looks like this:
> 
>     (guarded-block
>       (define x (random 10))
>       (guard (even? x) else
>         (log-info "x wasn't even, x = ~a" x)
>         -1)
>       (define y (random 10))
>       (guard (even? y) else
>         (log-info "y wasn't even, y = ~a" y)
>         -1)
>       (+ x y))
> 
>     Each |guard| clause contains a condition that must be true for
>     evaluation to proceed, and if it isn't true the block takes the else
>     branch and finishes. So the above would expand into this:
> 
>     (block
>       (define x (random 10))
>       (cond
>         [(not (even? x))
>          (log-info "x wasn't even, x = ~a" x)
>          -1]
>         [else
>          (define y (random 10))
>          (cond
>            [(not (even? y))
>             (log-info "y wasn't even, y = ~a" y)
>             -1]
>            [else (+ x y)])]))
> 
>     This part I got working pretty easily. Where I hit problems, and
>     where I'd like some help, is trying to extend this to support two
>     important features:
> 
>       *
> 
>         I should be able to define macros that /expand/ into |guard|
>         clauses. This is important because I want to implement a
>         |(guard-match <pattern> <expression> else <failure-body> ...)|
>         form that's like |match-define| but with an early exit if the
>         pattern match fails. I'd also really like to add a simple
>         |(guard-define <id> <option-expression> else <failure-body>
>         ...)| form that expects |option-expression| to produce an option
>         
> <https://gist.github.com/jackfirth/docs.racket-lang.org/rebellion/Option_Values.html>
>         (a value that is either |(present v)| or |absent|) and tries to
>         unwrap it, like the |guard let| construct in Swift
>         <https://www.hackingwithswift.com/sixty/10/3/unwrapping-with-guard>.
> 
>       *
> 
>         Begin splicing. The |begin| form should splice |guard|
>         statements into the surrounding body. This is really an offshoot
>         of the first requirement, since implementing macros that expand
>         to |guard| can involve expanding into code like |(begin (define
>         some-temp-value ...) (guard ...) (define some-result ...))|.
> 
>     Having been around the Racket macro block before, I know I need to
>     do some kind of partial expansion here. But honestly I can't figure
>     out how to use |local-expand|, |syntax-local-context|,
>     |syntax-local-make-definition-context|, and the zoo of related
>     tools. Can someone point me to some existing macros that implement
>     similar behavior? Or does anyone have general advice about what to
>     do here? I'm happy to share more examples of use cases I have for
>     |guarded-block| if that helps.
> 
> -- 
> You received this message because you are subscribed to the Google
> Groups "Racket Users" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to racket-users+unsubscr...@googlegroups.com
> <mailto:racket-users+unsubscr...@googlegroups.com>.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/racket-users/29552b1b-39e6-4608-86ae-466194f44263n%40googlegroups.com
> <https://groups.google.com/d/msgid/racket-users/29552b1b-39e6-4608-86ae-466194f44263n%40googlegroups.com?utm_medium=email&utm_source=footer>.

-- 
You received this message because you are subscribed to the Google Groups 
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to racket-users+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/racket-users/6f52b0cf-f805-b329-20c5-327903ae41b3%40trustica.cz.

Reply via email to