I was recently talking to one of programmers moving some Java code into Grace. He was refactoring Java code that uses “break” and “continue” statements, and wondered why Grace doesn’t support them. I answered that even if break and continue aren’t in the standard library the can certainly be written by a programmers.
The only infelicity is that rather than being commands, “break” and “continue” in Grace will be blocks, supplied as arguments into loops — to take one of these exits the program must apply the appropriate block argument. Here’s a simple loop using both “break” and “continue” — not exactly the best style but hopefully illustrativ
123456789 var foo := 0loopWithExits { break, continue ->foo := foo + 1print "A {foo}"if (foo == 2) then {continue.apply}if (foo == 4) then {break.apply}print "B {foo}"}
1
running this code produces:
A 1
B 1
A 2
A 3
B 3
A 4
as you might expect.
How is this implemented? Well first, here’s a simpler case: a “doWithExit” statement that takes a block and exits when the block is applied:
1234567 doWithExit { exit ->print "A"print "B"exit.applyprint "C"}print "Done"
1
this will print “A”, “B”, and then “Done” because “exit.apply” exits the block. The definition of the “doWithExit” statement is quite straightforward:
123 method doWithExit (block) {block.apply { return "doWithExit Return" }}
we just run the block supplied to the method, passing a second block as an argument, this second block calls return and so exits from the method, consequentially exiting the argument block early.
We can then build upon “doWithExit” to build up the “loopWithExits” statement above. The trick here is we inject two blocks into the looping code. The first of these, the “break” exit, returns from the whole “loopWithExits” statement, stopping the loop (just like C or Java’s break) — the implementation here is just the same as the single exit in the “doWithExit” method. The second injected argument comes from a “doWithExit” inside the loop proper to exit from only the code being looped. When that inner code returns, the control flows out of the “doWithExit” block, into the block controlled by the “loop” statement. Since we’re at the end of the loop, the control flows around to the next iteration of the loop — just like C or Java’s continue.
12345678 method loopWithExits (block) {def break = { return "loop return" }loop {doWithExit { continue ->block.apply( break, continue )}}}
This isn’t the simplest Grace code — frankly I wouldn’t teach it at all to first year students — but hopefully it does show the kind of things that programmers can write in a simple, language based on “a small number of underlying principles that can be applied uniformly”. In Grace, “loopWithExits” is just a method, like any other. The “loopWithExits” method is implemented using nothing but objects, blocks, requests, and returns, without any macro processing or explicit metaprogramming
Finally, it’s interesting to realize that we talked about this on the blog over a year ago and outlined what we hoped the basic Grace code for doing this would be. It’s gratifying to realize that the code above, that works on our current prototype Grace systems, is pretty much the same as last year’s sketches. And it’s even more gratifying to see last year’s sketches made flesh and running this year!