Re: [racket-users] Re: Help implementing an early return macro

2020-10-28 Thread Dominik Pantůček
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
>  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|
> 

Re: [racket-users] Help implementing an early return macro

2020-10-28 Thread David Storrs
I'm not sure if this is exactly what you want, but the handy module (which
I still need to split up into less of a Fibber McGee) includes handy/try.
This would let you do the following:

#lang racket

(require handy/try)

(define x (random 100))

; these are obviously silly functions that are only for the sake of example
(define (fails-first-check? x)   (= 0 (modulo x 10)))
(define (fails-second-check? x)  (= 0 (modulo x 7)))
(define (calculate-final-result z) 'final-result)

(displayln
 (try [(displayln (~a "x is: " x))
   (when (fails-first-check? x) (raise 'failed-first-check))
   (when (fails-second-check? x) (raise 'failed-second-check))
   (define z 'z)
   ; ...do something with z

   (calculate-final-result z)]
  [catch
((curry equal? 'failed-first-check)   (lambda (e) "failure #1"))
((curry equal? 'failed-second-check)  (lambda (e) "failure #2"))
(any/c(lambda (e)
'last-chance-processing-here))]))

It also supports pre and post checks:

#lang racket

(require handy/try)

(define x (random 100))

; real code would do something more sensible

(define (fails-first-check? x) (= 0 (modulo x 10)))
(define (fails-second-check?x) (= 0 (modulo x 7)))
(define (calculate-final-result z) 'final-result)

(displayln
 (try [pre (displayln "pre checks are guaranteed to happen")]
  [(displayln (~a "x is: " x))
   (when (fails-first-check? x) (raise 'failed-first-check))
   (when (fails-second-check? x) (raise 'failed-second-check))
   (define z 'z)
   ; ...do something with z

   (calculate-final-result z)]
  [catch
  ((curry equal? 'failed-first-check)   (lambda (e) "failure #1"))
  ((curry equal? 'failed-second-check)  (lambda (e) "failure #2"))
  (any/c(lambda (e)
'last-chance-processing-here))]
  [finally
   (displayln "do final cleanup here -- delete temp files, close
sockets, etc")
   (displayln "this is guaranteed to run even if an uncaught exception
is raised")
   ]))

The main body is required but 'pre', 'catch', and 'finally' are all
optional.  The whole thing is a dynamic-wind wrapped around a with-handlers
so pre and finally are guaranteed to execute no matter what.  pre is out of
scope for the main body which is suboptimal but if there was interest then
I might see about fixing that.

Unfortunately, like many of the submodules in handy 'try' has extensive
documentation in the comments but not in Scribble.  Again, I should get
around to that in my Copious Free Time.  For now, it's here:
https://github.com/dstorrs/racket-dstorrs-libs/blob/master/try.rkt



On Wed, Oct 28, 2020 at 9:48 AM Hendrik Boom  wrote:

> On Wed, Oct 28, 2020 at 03:54:29AM -0700, Jack Firth 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 ...)])])
>
> Perhaps you could use parendown
> https://docs.racket-lang.org/parendown/index.html
>
> #lang parendown racket/base
> (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,
> Frequently the lase element of a list is another list, and a large one
> at that.
> Using #/ makes that a kind of tail-recursive syntax and eliminates some
> explicit parentheses.
>
> Of course when you start using this it becomes so common that you'd like
> to drop the ugly #'s, but unfortunately, / is already taken.
>
> -- hendrik
>
> --
> 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/20201028134830.54plnv6nv5j3lcmt%40topoi.pooq.com
> .
>

-- 
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/CAE8gKodfdWieNE2iYqOO-Jajqv3E-%2B8MUhV-%2BwhPkShUvYouXA%40mail.gmail.com.


Re: [racket-users] Help implementing an early return macro

2020-10-28 Thread Hendrik Boom
On Wed, Oct 28, 2020 at 03:54:29AM -0700, Jack Firth 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 ...)])])

Perhaps you could use parendown
https://docs.racket-lang.org/parendown/index.html 

#lang parendown racket/base
(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,
Frequently the lase element of a list is another list, and a large one 
at that.
Using #/ makes that a kind of tail-recursive syntax and eliminates some 
explicit parentheses.

Of course when you start using this it becomes so common that you'd like
to drop the ugly #'s, but unfortunately, / is already taken.

-- hendrik

-- 
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/20201028134830.54plnv6nv5j3lcmt%40topoi.pooq.com.


Re: [racket-users] Help implementing an early return macro

2020-10-28 Thread Sam Caldwell
Ryan's solution is almost certain to be nicer, but if you do find yourself
needing internal definition contexts now or in the future, this is similar
to a case I ran into while adding `define` to a language implemented with
Turnstile.

I wrote a blog post outlining the solution [1], which I believe implements
the kind of local-expand loop asked about. You can skip to the "Internal
Definition Contexts" section if you don't care about the particulars of
Turnstile. There's some extra machinery for dealing with Turnstile-specific
things, but they should be pretty easy to remove and the basic ideas apply.
I didn't include splicing begins in the post, but it's comparatively
straightforward and in the actual implementation [2].

-Sam Caldwell

[1]
http://prl.ccs.neu.edu/blog/2018/10/22/defining-local-bindings-in-turnstile-languages/
[2]
https://github.com/tonyg/syndicate/blob/a6fc1f20e41fba49dc70d38b8c5047298e4b1811/racket/typed/core-types.rkt#L1220

On Wed, Oct 28, 2020 at 9:28 AM Ryan Culpepper 
wrote:

> This is a nice example of a macro design pattern that I think of as
> "partial expansion with trampolining". You don't need to deal with the
> internal definition context API, because you can return definitions to the
> macro expander, let it handle their interpretation, and then resume your
> work. Here's an implementation sketch:
>
> 1. First, make sure you're in an internal definition context:
> (guarded-block form ...) => (let () (guarded-block* form ...)). The
> guarded-block* helper macro has the invariant that it is always used in
> internal definition context.
> 2. If guarded-block* has at least one form, it partially expands it, using
> a stop list containing guard and Racket's primitive syntactic forms. Then
> it analyzes the partially-expanded form:
> - If it is a begin, it recurs with the begin's contents appended to the
> rest of its argument forms.
> - If it is a define-values or define-syntaxes form, it expands into (begin
> defn (guarded-block* form-rest ...)). The macro expander interprets the
> definition, adds it to the environment, etc. Then guarded-block* resumes
> with the rest of the forms (in the same definition context).
> - If it is a guard form, then you transform its contents and the rest of
> the forms into a cond expression, with a recursive call in the right place.
> - Anything else, assume it's an expression, and trampoline the same as for
> a definition.
>
> Also, because you're calling local-expand, you should disarm the result of
> local-expand and then call syntax-protect on the syntax you produce. If you
> don't disarm, then you might get "cannot use identifier tainted by macro
> transformer" errors. If you don't call syntax-protect, your macro can be
> misused to circumvent other macros' protection.
>
> I've attached an implementation.
>
> Ryan
>
>
> On Wed, Oct 28, 2020 at 11:54 AM Jack Firth  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
>>  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 

Re: [racket-users] Help implementing an early return macro

2020-10-28 Thread Ryan Culpepper
This is a nice example of a macro design pattern that I think of as
"partial expansion with trampolining". You don't need to deal with the
internal definition context API, because you can return definitions to the
macro expander, let it handle their interpretation, and then resume your
work. Here's an implementation sketch:

1. First, make sure you're in an internal definition context:
(guarded-block form ...) => (let () (guarded-block* form ...)). The
guarded-block* helper macro has the invariant that it is always used in
internal definition context.
2. If guarded-block* has at least one form, it partially expands it, using
a stop list containing guard and Racket's primitive syntactic forms. Then
it analyzes the partially-expanded form:
- If it is a begin, it recurs with the begin's contents appended to the
rest of its argument forms.
- If it is a define-values or define-syntaxes form, it expands into (begin
defn (guarded-block* form-rest ...)). The macro expander interprets the
definition, adds it to the environment, etc. Then guarded-block* resumes
with the rest of the forms (in the same definition context).
- If it is a guard form, then you transform its contents and the rest of
the forms into a cond expression, with a recursive call in the right place.
- Anything else, assume it's an expression, and trampoline the same as for
a definition.

Also, because you're calling local-expand, you should disarm the result of
local-expand and then call syntax-protect on the syntax you produce. If you
don't disarm, then you might get "cannot use identifier tainted by macro
transformer" errors. If you don't call syntax-protect, your macro can be
misused to circumvent other macros' protection.

I've attached an implementation.

Ryan


On Wed, Oct 28, 2020 at 11:54 AM Jack Firth  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
>  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))
> 

Re: [racket-users] Help implementing an early return macro

2020-10-28 Thread Philip McGrath
The most similar example that comes to mind is the way `for`-like forms
handle the `body-or-break` nonterminal to support `#:break` and `#:final`.
In particular, I think you would end up needing something analogous to these
semantics for definitions

:

> Among the `body`s, besides stopping the iteration and preventing later `
> body` evaluations, a `#:break guard-expr` or `#:final guard-expr` clause
> starts a new internal-definition context.
>

Personally, I'd also want to call your `guard` form something like
`return-when` and `return-unless`, with the relationship between the
condition expression and the right-hand side being the same as `when` and
`unless`.

-Philip


On Wed, Oct 28, 2020 at 7:25 AM Laurent  wrote:

> I've also had the same issues for a long time, and condd was almost good
> enough, but the #:do is too specific.
> But recently I'm using something much simpler (no continuation) and
> straightforward: cond/else
> https://github.com/Metaxal/bazaar/blob/master/cond-else.rkt
>
> By contrast to other approaches—which I usually try a few times then
> discard—I'm using this form regularly.
>
> It does precisely what you request (early return without continuations),
> but keeps both the readable cond and else structure while removing all
> unnecessary parentheses.
> Your example would look like this:
>
> (define x (random 10))
> (cond/else
>   [(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))
>
> You say you don't like the keywords, but I find that they actually
> increase readability.
>
> On Wed, Oct 28, 2020 at 10:54 AM Jack Firth  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
>>  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 

Re: [racket-users] Help implementing an early return macro

2020-10-28 Thread Laurent
I've also had the same issues for a long time, and condd was almost good
enough, but the #:do is too specific.
But recently I'm using something much simpler (no continuation) and
straightforward: cond/else
https://github.com/Metaxal/bazaar/blob/master/cond-else.rkt

By contrast to other approaches—which I usually try a few times then
discard—I'm using this form regularly.

It does precisely what you request (early return without continuations),
but keeps both the readable cond and else structure while removing all
unnecessary parentheses.
Your example would look like this:

(define x (random 10))
(cond/else
  [(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))

You say you don't like the keywords, but I find that they actually increase
readability.

On Wed, Oct 28, 2020 at 10:54 AM Jack Firth  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
>  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 
> else  ...) 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   else
> ...) form that expects option-expression to produce an
>option
>
> 
>(a value that is either (present v) or absent) and tries to unwrap it,
>like the guard let construct in Swift
>

[racket-users] Re: Help implementing an early return macro

2020-10-28 Thread Alex Harsanyi
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 
>  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  
> else  ...) 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   else 
> ...) form that expects option-expression to produce an 
>option 
>
> 
>  
>(a value that is either (present v) or absent) and tries to unwrap it, 
>like the guard let construct in Swift 
>.
>- 
>
>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 

[racket-users] Help implementing an early return macro

2020-10-28 Thread Jack Firth
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
 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 
else  ...) 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   else  ...)
   form that expects option-expression to produce an option
   

   (a value that is either (present v) or absent) and tries to unwrap it,
   like the guard let construct in Swift
   .
   -

   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.
To view this discussion on the web visit 

[racket-users] 2nd Call for Contributions: BOB 2021 [Feb 26, Deadline Nov 13]

2020-10-28 Thread Michael Sperber


Racket talks are extremely welcome at BOB!

 BOB Conference 2021
 "What happens when we use what's best for a change?"
  http://bobkonf.de/2021/cfc.html
 Berlin, February 26
Call for Contributions
 Deadline: November 13, 2020

You are actively engaged in advanced software engineering methods,
implement ambitious architectures and are open to cutting-edge
innovation? Attend this conference, meet people that share your goals,
and get to know the best software tools and technologies available
today. We strive to offer a day full of new experiences and
impressions that you can use to immediately improve your daily life as
a software developer.

If you share our vision and want to contribute, submit a proposal for
a talk or tutorial!

NOTE: The conference fee will be waived for presenters. Travel
expenses will not be covered (for exceptions see "Speaker Grants").
Online or Onsite

We do know yet whether BOB will happen onsite in Berlin or as an
online event. Should BOB happen online, we will likely ask for
pre-recorded talks to make room for questions and social interactions
during the actual conference day. (Of course, we'll provide assistance
making those recordings.) Tutorials will likely happen as a
live-session.

Speaker Grants
--

BOB has Speaker Grants available to support speakers from groups
under-represented in technology. We specifically seek women speakers,
speakers of color, and speakers who are not be able to attend the
conference for financial reasons.

Shepherding
---

The program committee offers shepherding to all speakers. Shepherding
provides speakers assistance with preparing their
sessions. Specifically:

- advice on structure and presentation
- review of talk slides
- assistance with recording
- review of recording, if applicable

Topics
--

We are looking for talks about best-of-breed software technology,
e.g.:

- functional programming
- persistent data structures and databases
- event-based modelling and architecture
- types
- formal methods for correctness and robustness
- abstractions for concurrency and parallelism
- metaprogramming
- probabilistic programming
- math and programming
- controlled side effects
- beyond REST and SOAP
- effective abstractions for data analytics
- … everything really that isn’t mainstream, but you think should be.

Presenters should provide the audience with information that is
practically useful for software developers.

We're especially interested in experience reports.  Other topics are
also relevant, e.g.:

- introductory talks on technical background
- overviews of a given field
- demos and how-tos

Requirements


We accept proposals for presentations of 45 minutes (40 minutes talk +
5 minutes questions), as well as 90 minute tutorials for
beginners. The language of presentation should be either English or
German.

Your proposal should include (in your presentation language of choice):

- An abstract of max. 1500 characters.
- A short bio/cv
- Contact information (including at least email address)
- A list of 3-5 concrete ideas of how your work can be applied in a developer's 
daily life
- additional material (websites, blogs, slides, videos of past presentations, …)
- Don't be confused: The system calls a submission event.

Organisation


- Direct questions to contact at bobkonf dot de
- Proposal deadline: November 13, 2020
- Notification: November 27, 2020
- Program: December 6, 2020

Submit here:

https://bobcfc.active-group.de/en/bob2021/cfp

Program Committee
-

(more information here: https://bobkonf.de/2020/programmkomitee.html)

- Matthias Fischmann, Wire
- Matthias Neubauer, SICK AG
- Nicole Rauch, Softwareentwicklung und Entwicklungscoaching
- Michael Sperber, Active Group
- Stefan Wehr, Hochschule Offenburg

Scientific Advisory Board

- Annette Bieniusa, TU Kaiserslautern
- Torsten Grust, Uni Tübingen
- Peter Thiemann, Uni Freiburg

-- 
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/y9lv9eusn1r.fsf%40deinprogramm.de.