A brief update on this: I went with Ryan's approach and used an 
implementation of guarded-block throughout Rebellion's codebase. You can 
see the diff here: https://github.com/jackfirth/rebellion/pull/466. A 
couple of things to note:

- I added a define/guard form that's like define, but with the function 
body implicitly wrapped with guarded-block.
- I tweaked the syntax of guard to allow either (guard <condition> else 
<body> ...) or (guard <condition> then <body> ...). If else is used, the 
guard is taken if the condition is false. If then is used, the guard is 
taken if the condition is true. This made it easier to organize my code 
such that the short-circuiting precondition checks came before what I 
considered to be the main branch of the function. With just the else case, 
sometimes I had to awkwardly negate things.

There's a few problems related to robustness I'd like to figure out 
eventually, before documenting this macro and publicly exposing it:

- It allows shadowing. If a variable is defined both before and after a 
guard statement, the definition after shadows the one before it, instead of 
raising a duplicate definition error.
- If any definitions raise syntax errors, the error is reported in terms of 
the expanded define-values form instead of the definition the user actually 
wrote.
- The last body form of a guarded block shouldn't be allowed to be a guard 
statement, and a good error message should be raised if a user does that. 
The current implementation doesn't check for that.

The last one is pretty easy to fix, but the other two I'm not sure how to 
fix.

On Sunday, November 1, 2020 at 1:04:15 PM UTC-8 gneuner2 wrote:

> On Sat, 31 Oct 2020 03:25:32 -0700 (PDT),
> "jackh...@gmail.com"
> <jackh...@gmail.com> wrote:
>
> >Wow, these are a lot of great responses. First of all, *awesome* job 
> Ryan. 
> >That implementation is exactly what I needed to figure out. I'm 
> definitely 
> >starting there first.
> >
> >> Are you looking for `let/ec`?
> >
> >I'd forgotten about that one. That has the *syntax* I want. However my 
> >issue with continuation-based approaches isn't the syntax, or even the 
> >performance. It's the semantics. What if someone writes code like this?
> >
> >(guarded-block
> > (define x (random 10))
> > (thread
> > (lambda ()
> > (guard (even? x) else #false)
> > ...)))
> >
> >If I implemented guarded-block in terms of let/ec, then what does this 
> code 
> >even *do*? I honestly don't know. It would probably run without error and 
> >do... something. 
>
> (let/ec return
> (thread
> (lambda ()
> (return -1)
> 42)))
>
> throws an error: "continuation application: attempt to jump into an
> escape continuation"
>
> There is a continuation barrier between the threads.
>
>
> However, let/cc works: e.g.,
>
> (let/cc return
> (thread
> (lambda ()
> (return -1)
> 42)))
>
> returns -1.
>
>
> >I am extremely sure that regardless of what it did, it 
> >would be confusing and it wouldn't solve any problem I had. 
>
> I am sure it would solve *some* problems.
>
>
> >I just flat out don't want to allow this or any related nonsense, 
> >such as:
> >
> >; Aliasing
> >(define return guard)
> >
> >; Higher-order usage
> >(map guard (some-list ...))
> >
> >; Capturing via closure
> >(guarded-block
> > (define (check-foo x) (guard (foo? x) else #false))
> > (check-foo ...)
> > ...)
> >
> >; Capturing via mutation
> >(set! previous-guard guard)
> >
> >; Oh great, now I have to think about even more continuation jumps
> >(dynamic-wind
> > (lambda () (guard ...))
> > (lambda () ...)
> > (lambda () ...))
> >
> >There might be valid use cases for some of these, but I certainly don't 
> >understand those use cases well enough to commit to a semantics for them.
>
> It always is safe to jump upwards OUT of a lower level computation.
> The sticky issues [and mental gyrations] with continuations all have
> to do with jumping downwards or sideways.
>
> I'm not sure what problems you might have with continuation barriers:
> the example of the thread shows that (upward-only) "escape"
> continuations don't work across threads ... but "full" continations do
> work, and would still work even if the threads were siblings rather
> than in a parent/child relationship.
>
> The issue for your purpose would be making sure the continuation is
> called from a position that is guaranteed to terminate the errant
> thread: you might need to recognize a thread as a special case, wrap
> it and (like an exception) catch/rethrow the continuation.
>
>
> Or, if you don't care about sibling threads, just use exceptions which
> always can be thrown upward out of child threads.
>
> George
>
>

-- 
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/d7714f66-d791-4ec8-a1c1-da8a9c761caen%40googlegroups.com.

Reply via email to