[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Continuation example

Jos Backus

3/2/2005 12:04:00 AM

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


11 Answers

ES

3/2/2005 6:18:00 AM

0

On Wed, March 2, 2005 12:04 am, Jos Backus said:
> 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 :)

<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




Robert Klemme

3/2/2005 10:22:00 AM

0


"Jos Backus" <jos@catnook.com> schrieb im Newsbeitrag
news:20050302000418.GB86042@lizzy.catnook.local...
> 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!

I don't really see the need for continuations here. Note that the handler
can do anything - even raise exceptions.

11:19:18 [robert.klemme]: /c/temp/ruby/nocont.rb
1
2
Error for element 3
3
4
5
Error for element 6
6
7
8
Error for element 9

Kind regards

robert



#!/usr/bin/env ruby

MAX_ERRORS = 3

class Reader
HANDLER = proc {|e| $stderr.puts "Error for element #{e.inspect}"}

def initialize(arr)
@arr = arr
end
def each(check, handler = HANDLER)
errors = 0
cc = nil
@arr.each do |elem|
if check.call(elem)
handler.call(elem)
errors += 1
break if errors >= MAX_ERRORS
end
yield elem
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}
rdr.each(check) do |elem|
puts elem
end


Mathieu Bouchard

3/2/2005 6:25:00 PM

0

Jos Backus

3/3/2005 12:03:00 AM

0

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 :)

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

--
Jos Backus
jos at catnook.com


ES

3/3/2005 4:51:00 AM

0

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 :)
>
> 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 :)
| |
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



Jos Backus

3/3/2005 5:34:00 PM

0

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 :)
> >
> > 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'.

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]

--
Jos Backus
jos at catnook.com


Lucille

2/7/2008 9:47:00 AM

0


"julien vancraenbroeck"


> Ah l?...........vous avez fait mouche !!
> Un moment de bont? et de douceur dans un monde de brutes !
oui.....?a fait du bien au coeur
> Mes deux grand-m?res habitaient la ville, mais ? quelques
> d?tails pr?s leurs gestes furent les m?mes que celles de la campagne.
> Votre
> message m'a permi de les revoir.
> J'arr?te ici car je commen?e ? mal lire mon >texte.
:-) il m'a fallut une heure pour m'en remettre
" si j'avais su que je l''aimais tant, je l''aurais aim?e d'avantage"......


Lucille

2/7/2008 9:48:00 AM

0


"alien"

> bof, c'est pas ? moi qu'il faut dire merci,
> c'est un texte trouv? sur le net et d'un auteur >inconnu ;-)
mais c'est gr?ce ? toi que nous sommes ?mu
:-)


coma

2/7/2008 9:43:00 PM

0


">> Ah là...........vous avez fait mouche !!
>> Un moment de bonté et de douceur dans un monde de brutes !
>> Mes deux grand-mères habitaient la ville, mais à quelques
>> détails près leurs gestes furent les mêmes que celles de la campagne. Votre
>> message m'a permi de les revoir.
>> J'arrête ici car je commençe à mal lire mon texte.
>> Merci à vous !
>> Cordialement
>> Julien
>>
>>
>>
> bof, c'est pas à moi qu'il faut dire merci,
> c'est un texte trouvé sur le net et d'un auteur inconnu ;-)

L'auteur n'est peut être plus inconnue.
A vous de voir.

http://poeme-grand-mere.jepoeme.com/discussion-21834-Souvenons_nous_du_Tablier_de_Grand_M...

Coma

vanille

2/8/2008 6:34:00 AM

0

super ! merci Coma !
par la même occasion, on peut découvrir d'autres textes de Sherazade
vanille
"coma" <coma@skynet.be> a écrit dans le message de news: 47ab7b4d$0$2952$ba620e4c@news.skynet.be...

">> Ah là...........vous avez fait mouche !!
>> Un moment de bonté et de douceur dans un monde de brutes !
>> Mes deux grand-mères habitaient la ville, mais à quelques
>> détails près leurs gestes furent les mêmes que celles de la campagne. Votre
>> message m'a permi de les revoir.
>> J'arrête ici car je commençe à mal lire mon texte.
>> Merci à vous !
>> Cordialement
>> Julien
>>
>>
>>
> bof, c'est pas à moi qu'il faut dire merci,
> c'est un texte trouvé sur le net et d'un auteur inconnu ;-)

L'auteur n'est peut être plus inconnue.
A vous de voir.

http://poeme-grand-mere.jepoeme.com/discussion-21834-Souvenons_nous_du_Tablier_de_Grand_M...

Coma