Continuation example

Here's an example I came up with today while investigating continuations. Hope
this is useful to somebody trying to understand and use them. Feedback
welcome!

Output:

lizzy:~% ./continuation-example.rb
1
2
exception seen, performing error handling (elem=3)
continuing
4
5
exception seen, performing error handling (elem=6)
continuing
7
8
exception seen, performing error handling (elem=9)
continuing
lizzy:~%

Code:

#!/usr/bin/env ruby

# Have you ever had the problem of wanting to report some error condition from
# deep down within a set of nested method calls, but also wanting processing
# to continue right after the spot where the error occurred, after the error
# is reported and handled in the caller?

···

#
# While there are other ways to do this (e.g. using callbacks), this example
# shows how to do this using exceptions and continuations. The trick is to
# have the exception propagate the continuation to the appropriate caller
# which can then handle the error (print an error message, say) and cause
# processing to continue inside the method that saw the error by calling the
# continuation passed up.
#
# This code uses a Reader class instance to process a list of elements
# (numbers) using a screening function. The processing is facilitated using
# the Reader.each method. Inside this method, if the element passes screening
# it is yielded to the caller, else an exception is raised. The caller
# processes the exception, then causes processing to continue inside the
# Reader.each method, while passing a hint into it telling it how to continue.
# In this particular example, the screening test consists of checking whether
# a list element is divisible by 3, if which case it is considered "bad".
# Also, when a maximum of 3 errors is seen (as indicated by the caller)
# processing of list elements stops.

MAX_ERRORS = 3

class CCException < Exception
  attr_reader :cc, :elem
  def initialize(cc, elem)
    @cc, @elem = cc, elem
  end
end

class Reader
  def initialize(arr)
    @arr = arr
  end
  def each(check)
    cc = nil
    @arr.each do |elem|
      if check.call(elem)
  ok_to_continue = Kernel.callcc do |cc|
    raise CCException.new(cc, elem)
  end
  break unless ok_to_continue
      else
  yield elem
      end
    end
  end
end

list = [1,2,3,4,5,6,7,8,9,10,11,12]
rdr = Reader.new(list)
errors = 0
# Returns whether we consider an element 'bad'
check = proc {|elem| elem % 3 == 0}
begin
  rdr.each(check) do |elem|
    puts elem
  end
rescue CCException => exc
  errors += 1
  puts "exception seen, performing error handling (elem=#{exc.elem})"
  # ...error handling...
  puts "continuing"
  exc.cc.call(errors < MAX_ERRORS)
end

exit

--
Jos Backus
jos at catnook.com

Here's an example I came up with today while investigating continuations. Hope
this is useful to somebody trying to understand and use them. Feedback
welcome!

Hehe, strange, I was lamenting the state of exception handling on the bus just
yesterday. I was thinking along the same lines, some sort of detached exception
handling mechanism. The handler would be defined elsewhere (so as to not clutter
the method) but would probably have to have access to the method's context.

Ruby's positioned well as far as 'normal' exception handling with having allowed
def ... rescue along with the explicit mode, and particularly with incorporating
'retry' (if you don't try to handle the exception, you could just fail with an
ERRNO anyway:). Now if only the default handling were detached from the method..

It'd be great fun if exception handling could *literally* be described
parallel to the method :slight_smile:

<pre>

def foo() handler
  # Do something
  do_something() fail? do fix_do_something(); retry :once; end

  # Other ops
  if something_else
    do_something_else() fail? do fix_do_something_else(); retry 2; end
  end
end end

</pre>

Good work.

Output:

lizzy:~% ./continuation-example.rb
1
2
exception seen, performing error handling (elem=3)
continuing
4
5
exception seen, performing error handling (elem=6)
continuing
7
8
exception seen, performing error handling (elem=9)
continuing
lizzy:~%

Code:

#!/usr/bin/env ruby

# Have you ever had the problem of wanting to report some error condition from
# deep down within a set of nested method calls, but also wanting processing
# to continue right after the spot where the error occurred, after the error
# is reported and handled in the caller?
#
# While there are other ways to do this (e.g. using callbacks), this example
# shows how to do this using exceptions and continuations. The trick is to
# have the exception propagate the continuation to the appropriate caller
# which can then handle the error (print an error message, say) and cause
# processing to continue inside the method that saw the error by calling the
# continuation passed up.
#
# This code uses a Reader class instance to process a list of elements
# (numbers) using a screening function. The processing is facilitated using
# the Reader.each method. Inside this method, if the element passes screening
# it is yielded to the caller, else an exception is raised. The caller
# processes the exception, then causes processing to continue inside the
# Reader.each method, while passing a hint into it telling it how to continue.
# In this particular example, the screening test consists of checking whether
# a list element is divisible by 3, if which case it is considered "bad".
# Also, when a maximum of 3 errors is seen (as indicated by the caller)
# processing of list elements stops.

MAX_ERRORS = 3

class CCException < Exception
  attr_reader :cc, :elem
  def initialize(cc, elem)
    @cc, @elem = cc, elem
  end
end

class Reader
  def initialize(arr)
    @arr = arr
  end
  def each(check)
    cc = nil
    @arr.each do |elem|
      if check.call(elem)
  ok_to_continue = Kernel.callcc do |cc|
    raise CCException.new(cc, elem)
  end
  break unless ok_to_continue
      else
  yield elem
      end
    end
  end
end

list = [1,2,3,4,5,6,7,8,9,10,11,12]
rdr = Reader.new(list)
errors = 0
# Returns whether we consider an element 'bad'
check = proc {|elem| elem % 3 == 0}
begin
  rdr.each(check) do |elem|
    puts elem
  end
rescue CCException => exc
  errors += 1
  puts "exception seen, performing error handling (elem=#{exc.elem})"
  # ...error handling...
  puts "continuing"
  exc.cc.call(errors < MAX_ERRORS)
end

exit

Jos Backus

E

···

On Wed, March 2, 2005 12:04 am, Jos Backus said:

You need "around-methods", an AOP concept taken from CommonLISP. You
define the error handler as an around-method called foo, put a
begin/rescue/ensure block that normally just runs "super", which calls the
supermethod, which is not necessarily in a superclass. In this case, what
gets run is the regular-method foo of the same class.

You can achieve a similar effect without AOP by putting error handling in
foo() and the code in foo_real().

An alternative is to have two classes instead, and put the error-handling
in the subclass. This is what MetaRuby (matju's) did to achieve
contract-checking. However it doesn't lend itself to subclassing, as the
first foo is executed inside the error handling (the 2nd foo) but the 3rd
foo is executed outside of it.

···

On Wed, 2 Mar 2005, ES wrote:

It'd be great fun if exception handling could *literally* be described
parallel to the method :slight_smile:

_____________________________________________________________________
Mathieu Bouchard -=- Montréal QC Canada -=- http://artengine.ca/matju

[snip]

It'd be great fun if exception handling could *literally* be described
parallel to the method :slight_smile:

I'm still trying to wrap my head around this. It sounds like you want a
special kind of method attribute that is itself a method that is invoked under
certain circumstances?

<pre>

def foo() handler
  # Do something
  do_something() fail? do fix_do_something(); retry :once; end

  # Other ops
  if something_else
    do_something_else() fail? do fix_do_something_else(); retry 2; end
  end
end end

</pre>

Good work.

Thanks.

Two nits I found after sending:

> # a list element is divisible by 3, if which case it is considered "bad".

                                        in

···

On Wed, Mar 02, 2005 at 03:18:01PM +0900, ES wrote:

On Wed, March 2, 2005 12:04 am, Jos Backus said:
> def each(check)
> cc = nil <-- not needed
> @arr.each do |elem|

--
Jos Backus
jos at catnook.com

[snip]

It'd be great fun if exception handling could *literally* be described
parallel to the method :slight_smile:

I'm still trying to wrap my head around this. It sounds like you want a
special kind of method attribute that is itself a method that is invoked under
certain circumstances?

Oh, no, more of a concrete separation of exception handling from the method
body: conceptually think IDE-level[1]. You specify the correct code path in
your method (i.e. write it like nothing could ever go wrong), then double-click
on the method name and it brings up a little window where you can enter all
of the exception handling code[2]. Obviously this in particular is more a
convenience/clutterlessness enhancement, but it might help in redirecting
exception processing back to 'handling' instead of 'reporting'.

[1] IDE used just for visualization of example, no reason this couldn't
    be used at any level from language specification up.
[2] Exception handling, says I, should exist as a meta-level in a given
    program without the access restraints placed on normal code.

So here,

  This is the method This is the error handling, tab-separated :slight_smile:
          > >
          V V

<pre>

def foo() handler
  # Do something
  do_something() fail? do fix_do_something(); retry :once; end

  # Other ops
  if something_else
    do_something_else() fail? do fix_do_something_else(); retry 2;
end
  end
end end

</pre>

Good work.

Thanks.

Two nits I found after sending:

> # a list element is divisible by 3, if which case it is considered "bad".

                                        in

> def each(check)
> cc = nil <-- not needed
> @arr.each do |elem|

Jos Backus

E

···

On Thu, March 3, 2005 12:02 am, Jos Backus said:

On Wed, Mar 02, 2005 at 03:18:01PM +0900, ES wrote:

On Wed, March 2, 2005 12:04 am, Jos Backus said:

Okay, I think I understand the IDE analogy. So these around-methods are a way
of implementing this at the language level (I know nothing about AOP btw)?
Sounds interesting...

[example snipped]

···

On Thu, Mar 03, 2005 at 01:51:10PM +0900, ES wrote:

On Thu, March 3, 2005 12:02 am, Jos Backus said:
> On Wed, Mar 02, 2005 at 03:18:01PM +0900, ES wrote:
>> On Wed, March 2, 2005 12:04 am, Jos Backus said:
> [snip]
>> It'd be great fun if exception handling could *literally* be described
>> parallel to the method :slight_smile:
>
> I'm still trying to wrap my head around this. It sounds like you want a
> special kind of method attribute that is itself a method that is invoked under
> certain circumstances?

Oh, no, more of a concrete separation of exception handling from the method
body: conceptually think IDE-level[1]. You specify the correct code path in
your method (i.e. write it like nothing could ever go wrong), then double-click
on the method name and it brings up a little window where you can enter all
of the exception handling code[2]. Obviously this in particular is more a
convenience/clutterlessness enhancement, but it might help in redirecting
exception processing back to 'handling' instead of 'reporting'.

--
Jos Backus
jos at catnook.com