From: Brian Candler Date: 2009-01-29T18:33:58+09:00 Subject: Re: replacing callcc by catch/throw Thomas Hafner wrote: > Run the program and let read it from standard input one line > consisting of an opening and a closing parenthesis, nothing else: > () > > The output will be an empty line. (Well, not very exiting, but I've > looked for the simpliest example.) OK, I see the problem now. If you use the same symbol everywhere, this happens: def document ... loop do catch(:foo) do ... multiline do ... throw(:foo) <-- invoked when block is yielded to However, multiline calls multiline_tail, which has its own catch/throw pair. If it also uses catch(:foo) then the throw will be caught inside there instead. So I think the solution is easy: use two different symbols, e.g. catch(:next_document) in document, and catch(:next_multiline_tail) in multiline_tail. This works for the trivial document you provided. You might want to try a more substantial example though :-) >> Or indeed, what's wrong with using loop { .... next ... } ? > > Just replacing ``throw'' by ``next'' doesn't help in these situations: > loop { ... multiline { ... next ... } ... } > loop { ... character { ... next ... } ... } > ^^^ > executed despite ``next''! > > ``next'' will then quit only the innermost block, that's not enough. You're right, and realising that 'next' was being executed inside an inner block is what put me on the right track. Perl has statement labels for this: OUTER: while(1) { ... next OUTER } but I don't think Ruby has a direct equivalent. As to the original question: I think the catch/throw version of the code is far, far clearer than callcc. Continuations are strange beasts: they are entire execution contexts which can be squirreled away and restarted at any point in the future. They are essentially threads, albeit non-preemptive. A program which uses callcc can be very hard to understand. Your example isn't, but only when you realise that |proceed| is (a) a local variable, (b) reset each time around the loop, and (c) is never passed to any other method. This guarantees that previous continuation objects will not be invoked later and will be garbage-collected. But you have to analyse the code very carefully to draw this conclusion, whereas a loop plus throw/catch is pretty obvious (I think). Regards, Brian. P.S. If I understand your code right, I think you can simplify multiline to: def multiline( input, p, &blk ) '(' == input[p] && multiline_tail( input, p+1, &blk ) end or def multiline( input, p, &blk ) multiline_tail( input, p+1, &blk ) if '(' == input[p] end All that a block like { |p2,v2| yield p2,v2 } does is to relay the call back to the original block, so why not just yield to the original block directly? You were using the 'return' statement too, which has special use within a block, but I think it may not be needed here. -- Posted via http://www.ruby-forum.com/.