Why does Ruby have callcc?

J

Jim Bob

I understand, in a woozy sort of way, what callcc does. What I
don't understand is what it's really good for, and furthermore, what
it's doing in Ruby.

It seems like all the explanations of callcc, in its various
implementations, stick to pretty simple examples, and almost always with
the proviso "you could more easily do this another way, but...". The
exception might be with regard to Scheme, which, I guess, lacks a
catch/throw-like mechanism, so that callcc might be the best way to
escape from a deeply nested loop. Ruby, though, obviously has catch and
throw.

With some other languages, I wouldn't think much of the inclusion of
such an apparently abstruse feature. But, from my reading of this list,
it seems like Matz is pretty conservative about adding features that
won't get used, for whatever reason. I can only guess that he'd be twice
less likely to add features that don't get used and are just plain
*weird*, as callcc seems to me even after having spent no short amount
of time trying to wrap my thick head around it.

So, how'd it get there? Is there some killer use for callcc that I'm
missing? Was it just for the "gee whiz" factor?
 
H

Harry Ohlsen

Jim said:
So, how'd it get there? Is there some killer use for callcc that I'm
missing? Was it just for the "gee whiz" factor?

I'd be interested to read about some real-world examples where people have used callcc, not just to try it out, but because it was the "right" way to do something (for efficiency, or code brevity or whatever reason).

So, as opposed to the examples we find on some of the Ruby web pages, which are, as Bob said, effectively tutorials on how one *might* use callcc, can someone give us some examples of production code they've written that uses continuations?
 
B

Brian Candler

I'd be interested to read about some real-world examples where people have
used callcc, not just to try it out, but because it was the "right" way to
do something (for efficiency, or code brevity or whatever reason).

I think Seaside/Borges falls into this category.

I haven't had a chance to use it in anger, but I think the idea is that
continuations can represent CGI session state; you code can be written in a
linear fashion (e.g. display page, fetch response, display next page, fetch
next response etc) without any explicit session maintenance. Furthermore, by
retaining continuation objects which represent earlier pages, it will work
even if the user clicks 'back' in their browser and re-enters data in a
previous form.

I'd have a number of concerns in a practical application - I'd want old
continuations to expire and be garbage-collected so that they don't cause
infinite memory growth - but otherwise it sounds like a great way to write
web applications.

Regards,

Brian.
 
H

Harry Ohlsen

Brian said:
I think Seaside/Borges falls into this category.

Do you have any URLs for this? I did a quick Google, but found a Smalltalk site. As far as I can tell, Seaside was some Ruby code that ended up in Smalltalk as Borges?

H.
 
R

Robert Klemme

Harry Ohlsen said:
I'd be interested to read about some real-world examples where people
have used callcc, not just to try it out, but because it was the "right"
way to do something (for efficiency, or code brevity or whatever reason).

Standalone iterators. Jim Weirich posted some code here about 1 or 2
months ago. Maybe you'll find it here: http://onestepback.org/
So, as opposed to the examples we find on some of the Ruby web pages,
which are, as Bob said, effectively tutorials on how one *might* use
callcc, can someone give us some examples of production code they've
written that uses continuations?

http://www.rubygarden.org/ruby?ContinuationExplanation

robert
 
B

Brian Candler

Do you have any URLs for this? I did a quick Google, but found a
Smalltalk site. As far as I can tell, Seaside was some Ruby code that
ended up in Smalltalk as Borges?

No, it was the other way round :) Anyway,

http://raa.ruby-lang.org/list.rhtml?name=borges links to
http://segment7.net/ruby-code/borges/borges.html

which in turn links to information on Seaside. Also search for 'seaside' or
'borges' at http://ruby-talk.org/ as it's been discussed before.

ISTR it uses Webrick, and since that's now part of ruby-1.8.0, it should be
easier to install.

Cheers,

Brian.
 
B

Brian Candler

ISTR it uses Webrick, and since that's now part of ruby-1.8.0, it should be
easier to install.

Just installed, it was a doddle: you need 'installpkg-0.0.1' and
'borges-0.2.0', then

cd /usr/local/share/examples/ruby/borges
ruby counter.rb

and then point your web browser at
http://yourmachine:7000/counter

It doesn't work with 'back' in the way I thought though; if I increment to
4, click back a few times, then click 'inc' on 1, I still get 5. But I have
a lot to learn about this framework :)

Also I couldn't get pagecounter.rb to work as-is; you need to change
"counter2" to something which doesn't end with a number, e.g. "pagecounter"
because a request to http://foo:7000/counter2 seems to be interpreted as
'counter'.

Cheers,

Brian.
 
Y

Yukihiro Matsumoto

Hi,

In message "Re: Why does Ruby have callcc?"

|> So, how'd it get there? Is there some killer use for callcc that I'm
|> missing? Was it just for the "gee whiz" factor?
|
|I can't explain the "how'd it get there" part; I'll leave that to Matz
|and friends.

The official answer is "why not". I provide the features, you use
them.
matz.
 
J

Julian Fitzell

Brian said:
I think Seaside/Borges falls into this category.

Yup, absolutely.
I haven't had a chance to use it in anger, but I think the idea is that
continuations can represent CGI session state; you code can be written in a
linear fashion (e.g. display page, fetch response, display next page, fetch
next response etc) without any explicit session maintenance. Furthermore, by
retaining continuation objects which represent earlier pages, it will work
even if the user clicks 'back' in their browser and re-enters data in a
previous form.

That's a pretty decent summary.
I'd have a number of concerns in a practical application - I'd want old
continuations to expire and be garbage-collected so that they don't cause
infinite memory growth - but otherwise it sounds like a great way to write
web applications.

They certainly are expired and GC'ed. And you can control how many
continuations you keep around for a particular session. You can also
control how long sessions hang around - or you could swap them to disk
and keep them forever. Again, this is all speaking about the Smalltalk
version since I haven't seent he Ruby code in quite some time and it may
well be true that you have less control of these things there.

If anyone's interested in discussing Seaside or the benefits of
continuations further, the Seaside (Smalltalk people but others welcome)
community is all on the mailing list which you can sign up for at:

http://lists.squeakfoundation.org/listinfo/seaside

Cheers,

Julian
 
B

Ben Giddings

I have been interested in these continuation-thingys for a while now, so now
that the subject came up, I decided to take a good look.

I now think I understand how they work, and when they might be used, but that
still leaves a few questions:

1) Why do they have the strange syntax they have
2) Why don't they have more "meat" to them?

I found a bunch of websites all talking about continuations:

http://www.ai.mit.edu/~gregs/ll1-discuss-archive-html/threads.html#01372
http://www.ps.uni-sb.de/~duchier/python/continuations.html
...

The summary is that Continuations are essentially "goto with parameters", or
even more simply, like setjmp, longjmp.

To do continuations, you need to save the state of the VM at the "site of the
continuation", and then be able to load that up when asked to do so. Given
that, I am puzzled by the syntax they have in Ruby. First of all, why the
block? In most of the examples I've seen, there doesn't seem to be a good
reason to have a block. The only reason the block is necessary seems to be
that the end of the block marks the location where Continuation#call will go
to. Also, why are they created by Kernel.callcc? Why not Continuation.new,
where the location from which to continue when the Continuation is called is
the next expression after .new.

The second issue is why Continuations have nothing but a .call method.
Because they should encapsulate things like stack and program-counter values
at the time of the call, it would seem to me like these are useful things to
have available.

Say, for instance, that Continuation had a "stack" accessor, it could return
something similar to Kernel.caller(). Continuation could also have a
"program_counter" attr_reader that would return some sort of binding object
(or a symbol or something) that would represent the location in the program
that would be jumped-to by .call. I think it would be interesting to be able
to manipulate Continuations in more ways than just .calling them. Maybe keep
an array of them around and jump to the one with the smallest stack? I don't
know.

While I'm busy redesigning the language, what if Kernel.caller could return an
array of Continuation objects, any of which could be .called? If I
understand things correctly calling a continuation is essentially no
different from returning from a method call. The PC is set to a certain
address, the stack is unwound one level, and the only difference is that a
certain value is held somewhere (in a register?)

Anyhow, before today I really didn't know anything about Continuations, so
maybe what I'm saying makes no sense at all, but I'd like to hear any
comments on the subject.

Ben
 
D

Dan Sugalski

I have been interested in these continuation-thingys for a while now, so now
that the subject came up, I decided to take a good look.

I now think I understand how they work, and when they might be used, but that
still leaves a few questions:

1) Why do they have the strange syntax they have

They don't--there's no inherent strange syntax to them. The syntax
comes from the language implementing the continuation semantics.
2) Why don't they have more "meat" to them?

Because there's not much to them. Really, there isn't. Continuations
(or, as I have recently been scolded about, first class
continuations) are pretty simple things.
I found a bunch of websites all talking about continuations:

http://www.ai.mit.edu/~gregs/ll1-discuss-archive-html/threads.html#01372
http://www.ps.uni-sb.de/~duchier/python/continuations.html
...

The summary is that Continuations are essentially "goto with parameters", or
even more simply, like setjmp, longjmp.

Well... no. that's not quite right. There's rather more to
continuations than just that. Continuations are more a Location with
Environment and History. Closures are Locations with Environment, and
Functions are just Locations.
The second issue is why Continuations have nothing but a .call method.
Because they should encapsulate things like stack and program-counter values
at the time of the call, it would seem to me like these are useful things to
have available.

It's generally considered Really Evil to look at anything inside a
continuation. Darned useful, though...
--
Dan

--------------------------------------"it's like this"-------------------
Dan Sugalski even samurai
(e-mail address removed) have teddy bears and even
teddy bears get drunk
 
H

Hal E. Fulton

----- Original Message -----
From: "Dan Sugalski" <[email protected]>
To: "ruby-talk ML" <[email protected]>
Sent: Wednesday, August 06, 2003 5:45 PM
Subject: Re: Why does Ruby have callcc?

They don't--there's no inherent strange syntax to them. The syntax
comes from the language implementing the continuation semantics.

Well, be fair. He's talking about continuations *in Ruby* --
why do they look they way they do from a language user's
perspective?

And I have to agree with him -- it's unintuitive to me why
there should be a block as opposed to just a "Continuation.new"
call. And likewise I don't understand why callcc is in Kernel.

Hal
 
B

Ben Giddings

They don't--there's no inherent strange syntax to them. The syntax
comes from the language implementing the continuation semantics.

Like Hal said, I meant why do they have the strange syntax they do "in Ruby".
There just doesn't seem to be a good reason for a block. It also seems
strange to have a class with no constructor that can only be created by a
Kernel method.

Is there some reason that Continuation.new couldn't work?
Well... no. that's not quite right. There's rather more to
continuations than just that. Continuations are more a Location with
Environment and History. Closures are Locations with Environment, and
Functions are just Locations.

Ok, thanks. That's a good simplification of what I think I meant to say. :)
It's generally considered Really Evil to look at anything inside a
continuation. Darned useful, though...

I like being evil. ;)

I can imagine it being truly evil to be able to *change* a Continuation, but
it doesn't strike me as terribly evil to look inside them. Even if the only
(programmer accessible) extra information them was similar to what you get
out of Kernel.caller. That way you could say something like:

puts "About to call the continuation %p" % the_continuation

And get something useful out of it.

Comments?

Ben
 
E

Eric Hodel

--J2pR2RtFp0tAjF3C
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable
=20
Like Hal said, I meant why do they have the strange syntax they do "in Ru= by". =20
There just doesn't seem to be a good reason for a block. It also seems= =20
strange to have a class with no constructor that can only be created by a= =20
Kernel method.

The advantage to the block is that it allows you to do some stuff
with the continuation (like saving this potential chain of execution)
without continuaing. Not having the block makes things much more
difficult.

Take a look at the guts of Borges, particuarly Session.rb.

http://segment7.net/ruby-code/borges/borges.html


If you read below, you'll see the continuation captures what you're
doing in respond, and saves it, and doesn't return until you invoke
that continuation. The block allows you to do some stuff, *then*
invoke, but only if you really want to in the convenient ruby
metaphor.

You may have to read more of the code to really understand what's
going on here, I only understand enough of it to have an intuitive
feel for the deep magic that's going on inside. I couldn't rewrite it
from scratch.

class Session

# ...

def handle_request_intern(request, response)
@response =3D response
@current_key =3D request.action_key
callcc do |cc|
@short_jmp =3D cc # *** note this
if cont =3D @continuations[@current_key]
# if there is a continuation for current_key, invoke
cont.call(request)
else
# handle unknown requests
unknown_request(request)
end
end
@last_key =3D @current_key
end

# ...

def respond
callcc do |cc|
@response.action_key =3D @continuations.store(cc)
yield(@response)
@short_jmp.call # *** is used here
end
end

end

And this code gets used in Renderer.rb:

def render_response
@callbacks =3D CallbackStore.new
request =3D respond do |res|
@response =3D @stream =3D res
yield(self)
end
@callbacks.process_callbacks(request)
end

def text_input(value, attrs=3D{}, &update)
attrs["value"] =3D value
# This only gets invoked if this action is chosen.
attrs["name"] =3D @callbacks.register_callback(update)
input("text", attrs)
end
Is there some reason that Continuation.new couldn't work?

I don't know if this is 100% correct:

class Continuation
def self.new
if block_given? then
callcc { |c| yield(c) }
else
callcc { |c| return c }
end
end
end


--=20
Eric Hodel - (e-mail address removed) - http://segment7.net
All messages signed with fingerprint:
FEC2 57F1 D465 EB15 5D6E 7C11 332A 551C 796C 9F04


--J2pR2RtFp0tAjF3C
Content-Type: application/pgp-signature
Content-Disposition: inline

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.2 (FreeBSD)

iD8DBQE/MZJ4MypVHHlsnwQRAqDqAKCUF+D2Vtm8aZ5xpsAxq4/Xfyt9HwCfdNgY
KmDMkK1c+XXDc/nfNCgEJw8=
=+fMR
-----END PGP SIGNATURE-----

--J2pR2RtFp0tAjF3C--
 
E

Eric Hodel

--V3eawNQxI9TAjvgi
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable
Ben Giddings ([email protected]) wrote:
=20
=20
I don't know if this is 100% correct:
=20
class Continuation
def self.new
if block_given? then
callcc { |c| yield(c) }
else
callcc { |c| return c }
end
end
end

Oh yes, and there is this problem:

irb(main):007:0> c =3D Continuation.new; puts 'hi'
hi
=3D> nil
irb(main):008:0> c.call
hi
=3D> nil
irb(main):009:0> c.call
NoMethodError: undefined method `call' for nil
from (irb):9

Which is easily avoided with the block:

irb(main):012:0> cx =3D nil
=3D> nil
irb(main):013:0> callcc { |c| cx =3D c }; puts 'hi'
hi
=3D> nil
irb(main):014:0> cx.call
hi
=3D> nil
irb(main):015:0> cx.call
hi
=3D> nil
irb(main):016:0> cx.call
hi
=3D> nil

The block makes this easy.

--=20
Eric Hodel - (e-mail address removed) - http://segment7.net
All messages signed with fingerprint:
FEC2 57F1 D465 EB15 5D6E 7C11 332A 551C 796C 9F04


--V3eawNQxI9TAjvgi
Content-Type: application/pgp-signature
Content-Disposition: inline

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.2 (FreeBSD)

iD8DBQE/MZTsMypVHHlsnwQRAvimAKDposWIsPsibsrCZuigLmKHedLB5QCdFJ0T
hkuwKgMvGjc8Bn5ig2m+oR8=
=rFLu
-----END PGP SIGNATURE-----

--V3eawNQxI9TAjvgi--
 
J

Jim Weirich

Like Hal said, I meant why do they have the strange syntax they do "in Ruby".
There just doesn't seem to be a good reason for a block.

Continuations come out of a style of programming called Continuation
Passing Style (CPS) where the continuation to a function is explicitly
passed as an argument to the function. A continuation is essentially
the code that will be executed when the function returns. By explicitly
capturing a continuation and passing it as an argument, a normal
recursive function can be turned in to a tail recursive function and
there are interesting optimizations that can be done at that point. Dan
Sugalski has some writeups in his "What the Heck is ... " series at:
http://www.sidhe.org/~dan/blog.

Since a continuation is related to a function invocation, when you ask
for a continuation object you need to specify which function invocation
the continuation is for. callcc addresses this by invoking the block,
and passing the continuation of the block's invocation to the block
itself. Since callcc "knows" it needs the continuation before the block
is invoked, I suspect that it might be easier for the implementor than
if the continuation of just *any* function invocation could be grabbed.

I played around with a CPS solution to PragDave's Kata 2 (see
http://pragprog.com/pragdave/Practices/Kata/KataTwo.rdoc,v). You can
find my writeups at
http://onestepback.org/index.cgi/Tech/Programming/Kata. The third,
fourth and fifth entries deal with CPS, tail recursion and callcc.
 
Y

Yukihiro Matsumoto

Hi,

In message "Re: Why does Ruby have callcc?"

|How consistent is this w/ your "no featuritis" policy? In LL2 you had
|to explain why Ruby had callcc but no macros; your answer was that the
|latter are more easily abused ("too powerful" IIRC).

There's strong reason not to have macros in Ruby, as I believe.
Continuation does not harm you very much.

|Do you still believe that or does "I provide the features, you use them"
|represent a new policy?

I provide the features (which I choose), you use them.

matz.
 
M

Martin DeMello

Rudolf Polzer said:
case $lineno
when 100; puts "Hello World"
when 110; $i = 0
when 120; if $i > 10 then goto 160 end
when 130; puts "Current $i: %d" % [$i]
when 140; $i = $i + 1
when 150; goto 120
when 160; puts "Done!"
when 170; stop
end

Best piece of fallthrough abuse since Duff :)

martin
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top