[racket-dev] for loops with interleaved escape continuations

From: Jay Kominek (kominek at gmail.com)
Date: Sat Jun 28 00:54:03 EDT 2014

On Fri, Jun 27, 2014 at 8:40 PM, J. Ian Johnson <ianj at ccs.neu.edu> wrote:
> One is that #:when in for-clauses allows you to skip iterations, but you want it in the body. This is a cosmetic difference that you can get around by doing #:when (begin imperative-stuff-before-guard guard). If you want ecs to escape an entire for loop, that's what #:break is for. If you want ecs to escape even more for loops, well, that is an unplanned but a possibly useful feature for nested for loops.

Yes, #:when gets frustratingly close, but if
imperative-stuff-before-guard does something expensive (fetches the
ith web page, or what have you), I've got to repeat it in the body if
I actually want to use the value. Same issue with #:break and all the
other guards.

If I end up wanting #:when, #:break, and the body of the expression to
all make use of some expensive value, I'm stuck reproducing it. (Or
setting up some previous loop to make a bunch of promises that I can
force... bleh.)

Would I have more luck selling folks on some sort of #:let [id
val-expr] construct that could mix in with the sequences? That'd get
me most of what I was going for, and it is probably(?) a lot more
obvious how it should behave.

> The other is that this is not Rackety and thus not natively supported for the same reason there are no "return" statements. The for macros are so much more than Python's for - they can produce values. Don't throw everything into the void :)

Sorry, I should've put more than a moment's effort into my example. I
specifically tried to ensure that this can do more than Python's break
& continue! In fact, this allows poor boring "for" who normally
produces nothing but void to produce a useful value. Here are some
more examples, again contrived because I'm trying to keep them short,
but hopefully making more apparent the advantage over simple break &
continue:

(for (#:ec break
      [x (in-range 0 10)])
  (when    (= x 2)
    (break "found it!"))
  (printf "~a~n" x))

0
1
"found it!"


(for/fold ([x 0] [y 0])
          (#:ec break
       [i (in-range    0 10)]
           #:ec    continue)
  (when (= i 3)
    (continue (+ x 1000) (+ y 1000)))
  (when    (= i 7)
    (break (exact->inexact x) y))
  (values (add1 x) (add1 y)))

1006.0
1006

The inner escape continuation passes its values along to the next
iteration, while the outer one can return values for the entire
expression. And of course, it can be interleaved between sequences,
just like #:when and friends.

I was allowing the use of the continuations without arguments so that
there'd be a more obvious translation from languages with wimpy
control statements, but they raise the issue of what values should be
used by default.

(And to reiterate the current inadequacies, it doesn't behave at all
properly with for/list, for/vector or for/set.)

Thanks for looking.

-- 
Jay Kominek


Posted on the dev mailing list.