Why does Ruby have callcc?

  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.

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. :slight_smile:

It’s generally considered Really Evil to look at anything inside a
continuation. Darned useful, though…

I like being evil. :wink:

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

···

On Wed August 6 2003 6:45 pm, Dan Sugalski wrote:

Hi,

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.
···

In message “Re: Why does Ruby have callcc?” on 03/08/07, Mauricio Fernández batsman.geo@yahoo.com writes:

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

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.

···

On Wed, Aug 06, 2003 at 10:25:31PM +0900, Brian Candler wrote:

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

Brian Candler wrote:

Brian Candler wrote:

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?

Yeah, that’s the main site for Seaside, probably: beta4.com

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

Well, actually, Iowa was written in ruby before we moved to Squeak
Smalltalk to write Seaside/Borges. Then Avi ported part of Borges back
to ruby for RubyConf2002 to show that it could be done. We haven’t done
any work on the ruby Borges since but Eric has taken on the task of
developing it based on Seaside2.

···

On Wed, Aug 06, 2003 at 09:15:59PM +0900, Harry Ohlsen wrote:

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.

Well now, it’s not that it doesn’t scale - you just have to be careful
how you let it scale. There are several useful deployed applications
out there using Seaside. One in particular that Avi and I wrote has
been running for a year and a half with almost no maintenance on our
part. Now you’re right that you probably wouldn’t want to use it for a
site that received millions of hits in a day, but you have a quite a bit
of control over what state gets stored for backtracking and how many
previous page views you want to keep around for a session, etc. You can
definitely reasonably have a session kept under a couple of megs, which
could give you hundreds of concurrent users on a box with a Gig of ram.

Currently, Borges doesn’t support all that yet (some of it yes), I
add stuff to it as I need it.

Now I’m trying to remember the state of Borges (and the limitations of
the basic part he ported back to ruby for that talk) and I can’t
really… so it may be that what he demoed didn’t scale for some reason.
So it’s possible you’re remembering correctly :wink:

The mini-port Avi did could only run in a single thread, I have removed
this restriction, and added a few other things like session expiry.

The application ‘Webplayer’ at:

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

Is written using Borges and WEBrick. If you want a good example
application of how to write a Borges app, this is it. (And is a pretty
good web-based mp3 player controller.)

···

Julian Fitzell (julian@beta4.com) wrote:


Eric Hodel - drbrain@segment7.net - http://segment7.net
All messages signed with fingerprint:
FEC2 57F1 D465 EB15 5D6E 7C11 332A 551C 796C 9F04

  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.

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.

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 = response
@current_key = request.action_key
callcc do |cc|
@short_jmp = cc # *** note this
if cont = @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 = @current_key
end

def respond
callcc do |cc|
@response.action_key = @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 = CallbackStore.new
request = respond do |res|
@response = @stream = res
yield(self)
end
@callbacks.process_callbacks(request)
end

def text_input(value, attrs={}, &update)
attrs[“value”] = value
# This only gets invoked if this action is chosen.
attrs[“name”] = @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

···

Ben Giddings (ben@thingmagic.com) wrote:

On Wed August 6 2003 6:45 pm, Dan Sugalski wrote:


Eric Hodel - drbrain@segment7.net - http://segment7.net
All messages signed with fingerprint:
FEC2 57F1 D465 EB15 5D6E 7C11 332A 551C 796C 9F04

  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.

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.

Ah, OK. With the reference to the ll1 stuff, I was assuming you were
pondering the oddness of Scheme or (call-with-current-continuation
…). Sorry, misunderstood.

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?

That’d be for Matz to answer for sure, but the one thing about
continuations is you really can’t create them in library code. (Well,
you can but there are issues) Putting them in what’s essentially a
“guts of the interpreter” class makes some amount of sense.

It’s generally considered Really Evil to look at anything inside a
continuation. Darned useful, though…

I like being evil. :wink:

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.

I expect that’s just a SMOP. Throw something together and pass it by
Matz (or make it a module, I suppose) and see how things go.

···

At 8:12 AM +0900 8/7/03, Ben Giddings wrote:

On Wed August 6 2003 6:45 pm, Dan Sugalski wrote:

Dan

--------------------------------------“it’s like this”-------------------
Dan Sugalski even samurai
dan@sidhe.org have teddy bears and even
teddy bears get drunk

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.

···

On Wed, 2003-08-06 at 19:12, Ben Giddings wrote:

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.


– Jim Weirich jweirich@one.net http://onestepback.org

“Beware of bugs in the above code; I have only proved it correct,
not tried it.” – Donald Knuth (in a memo to Peter van Emde Boas)

Scripsit ille aut illa »Yukihiro Matsumoto« matz@ruby-lang.org:

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.

Naq gurl’er gur bayl jnl gb trg bhg bs fbzr rivy pbafgehpgf…
see my other posting.

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.

And I wouldn’t say featurities… continuations are ONE feature which
is a superset of coroutines, generators, setjmp/longjmp and the infamous
goto. And they’re hard enough to use to prevent BASIC-like GOTO spaghetti
code.

  • coroutines and generators are obviously NOT contained in the rest of Ruby.
  • goto is a subset of setjmp/jongjmp and therefore not in Ruby (or is it?)

Well, you all know that goto can be done using case, well, at least something
similar:

def goto(n)
$newlineno = n
end
def stop()
$newlineno = nil
end

$lineno = 0
while $lineno do
$newlineno = $lineno + 1
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
$lineno = $newlineno
end

But that’s no real goto, it’s just case misuse. And possible even in
case-less languages like Perl using hashes or if…

BTW: I know it hurts to read such code. Be happy I didn’t add “COME FROM”.

···

In message “Re: Why does Ruby have callcc?” > on 03/08/07, Mauricio Fernández batsman.geo@yahoo.com writes:


PLEASE DO NOT GIVE UP

matz@ruby-lang.org (Yukihiro Matsumoto) wrote in message news:1060273746.085536.28127.nullmailer@picachu.netlab.jp

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

is the rationale behind not using macros in Ruby documented somewhere?
is ther a relevant url? I would like to know the reasoning behind such a decision.

George Moschovitis

Oh yes, and there is this problem:

irb(main):007:0> c = Continuation.new; puts ‘hi’
hi
=> nil
irb(main):008:0> c.call
hi
=> 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 = nil
=> nil
irb(main):013:0> callcc { |c| cx = c }; puts ‘hi’
hi
=> nil
irb(main):014:0> cx.call
hi
=> nil
irb(main):015:0> cx.call
hi
=> nil
irb(main):016:0> cx.call
hi
=> nil

The block makes this easy.

···

Eric Hodel (drbrain@segment7.net) wrote:

Ben Giddings (ben@thingmagic.com) wrote:

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


Eric Hodel - drbrain@segment7.net - http://segment7.net
All messages signed with fingerprint:
FEC2 57F1 D465 EB15 5D6E 7C11 332A 551C 796C 9F04

Ah, OK. With the reference to the ll1 stuff, I was assuming you were
pondering the oddness of Scheme or (call-with-current-continuation
…). Sorry, misunderstood.

No worries. :slight_smile:

That’d be for Matz to answer for sure, but the one thing about
continuations is you really can’t create them in library code. (Well,
you can but there are issues) Putting them in what’s essentially a
“guts of the interpreter” class makes some amount of sense.

Then again, it could be argued that putting them in a module that’s
mixed in to every other class makes them seem like something you can
use with abandon…

I expect that’s just a SMOP. Throw something together and pass it by
Matz (or make it a module, I suppose) and see how things go.

Heh, I don’t think I’m ready to do that just yet. I’ve never even used
a continuation. I just thought it looked rather odd and thought I’d
bring it up for discussion among the wizards out there who actually
used it.

Ben

···

On Wednesday, August 6, 2003, at 07:58 PM, Dan Sugalski wrote:

Interesting… now I’m trying to understand what happens here when you
call Continuation.new. If I understand things correctly, with the
no-block version, new returns at the “return c” statement, then when
the continuation is called, it goes back to the end of that block, and
self.new returns… since that method overall has no return value, it
is nil, and so nothing is assigned. This one seems to work:

class Continuation
def self.new
retval = nil
if block_given?
callcc {|retval| return retval}
yield(retval)
else
callcc {|retval| return retval}
end
retval
end
end

This version does, however, redefine how the associated block is used
with callcc. With callcc, when you call the resulting continuation
object, you continue from the end of the block. With this version you
yield to the block.

Now I’m going to go try to understand the rest of the code you sent me.
:slight_smile:

Ben

···

On Wednesday, August 6, 2003, at 07:42 PM, Eric Hodel wrote:

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

Hi Jim,

Lots of good stuff in there. I found one of the most useful pages was:

http://www.sidhe.org/~dan/blog/archives/000213.html

Where he says:

“Well, if you remember with CPS, when you call a function you pass in a
continuation that represents the place you’re going to return to when
you’re done. In the normal case, a continuation is taken immediately
before the call (with a resume address for the code right after the
call, otherwise you’d call over and over and over…) and passed into
the function you’re calling”

Which is just what Ruby is doing… however most of the practical uses
of continuations I’ve seen don’t follow this rule well, they don’t use
"callcc" as a means of jumping out of the associated block, instead
they store the continuation in another variable outside the block,
leave the block, then use it somewhere else.

It looks like what he’s talking about is better implemented with
throw/catch in Ruby.

I’m not clear on how continuations help with tail recursions or tail
calls, however. Maybe there’s something subtle in there I’m missing.

Thanks,

Ben

Best piece of fallthrough abuse since Duff :slight_smile:

martin

···

Rudolf Polzer denshimeiru-sapmctacher@durchnull.ath.cx wrote:

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

puts “Hello World”
i = 0
callcc do | $goto160 |
callcc {| $goto120 |}
$goto160.call if i > 10 # 120;
puts “Current i: %d” % [i]
i = i + 1
$goto120.call
end
puts “Done!” # 160;

daz

···

“Rudolf Polzer” denshimeiru-sapmctacher@durchnull.ath.cx wrote:

  • coroutines and generators are obviously NOT contained in the rest of Ruby.
  • goto is a subset of setjmp/jongjmp and therefore not in Ruby (or is it?)

Well, you all know that goto can be done using case, well, at least something
similar:

def goto(n)
$newlineno = n
end
def stop()
$newlineno = nil
end

$lineno = 0
while $lineno do
$newlineno = $lineno + 1
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
$lineno = $newlineno
end

But that’s no real goto, it’s just case misuse. And possible even in
case-less languages like Perl using hashes or if…

PLEASE DO NOT GIVE UP

The closest to a rationale I’ve found:

http://www.bebear.net/ll2/LL2_Ruby.mov :slight_smile:

···

On Fri, Aug 08, 2003 at 07:08:43PM +0900, George Moschovitis wrote:

matz@ruby-lang.org (Yukihiro Matsumoto) wrote in message news:1060273746.085536.28127.nullmailer@picachu.netlab.jp

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

is the rationale behind not using macros in Ruby documented somewhere?
is ther a relevant url? I would like to know the reasoning behind such a decision.


_ _

__ __ | | ___ _ __ ___ __ _ _ __
'_ \ / | __/ __| '_ _ \ / ` | ’ \
) | (| | |
__ \ | | | | | (| | | | |
.__/ _,
|_|/| || ||_,|| |_|
Running Debian GNU/Linux Sid (unstable)
batsman dot geo at yahoo dot com

Linux is obsolete
– Andrew Tanenbaum

It’s important to note that the stuff referenced in the above link,
as well as most of the tech stuff on my blog, should be taken in the
context of implementing things. Generally, though not universally,
for Parrot. CPS, especially as an implementation scheme, is a very
particular use of continuations, and it’s probably not a good idea to
draw too many general conclusions from it.

···

At 2:05 PM +0900 8/7/03, Ben Giddings wrote:

Hi Jim,

Lots of good stuff in there. I found one of the most useful pages was:

http://www.sidhe.org/~dan/blog/archives/000213.html

Where he says:

“Well, if you remember with CPS, when you call a function you pass
in a continuation that represents the place you’re going to return
to when you’re done. In the normal case, a continuation is taken
immediately before the call (with a resume address for the code
right after the call, otherwise you’d call over and over and
over…) and passed into the function you’re calling”

Which is just what Ruby is doing… however most of the practical
uses of continuations I’ve seen don’t follow this rule well, they
don’t use “callcc” as a means of jumping out of the associated
block, instead they store the continuation in another variable
outside the block, leave the block, then use it somewhere else.

It looks like what he’s talking about is better implemented with
throw/catch in Ruby.


Dan

--------------------------------------“it’s like this”-------------------
Dan Sugalski even samurai
dan@sidhe.org have teddy bears and even
teddy bears get drunk

case $lineno
when 100; puts “Hello World”

[snip]

end

Best piece of fallthrough abuse since Duff :slight_smile:

Look more closely, it doesn’t use fallthrough. Ruby’s case is not the
same as C’s switch.

···

Martin DeMello (martindemello@yahoo.com) wrote:

Rudolf Polzer denshimeiru-sapmctacher@durchnull.ath.cx wrote:


Eric Hodel - drbrain@segment7.net - http://segment7.net
All messages signed with fingerprint:
FEC2 57F1 D465 EB15 5D6E 7C11 332A 551C 796C 9F04

puts “Hello World”
i = 0
callcc do | $goto160 |
callcc {| $goto120 |}
$goto160.call if i > 10 # 120;
puts “Current i: %d” % [i]
i = i + 1
$goto120.call
end
puts “Done!” # 160;

daz

Did you test this? I didn’t, but it appears to me that it will loop
forever. Although you put “i = i + 1”, when yyou then “goto 120”, won’t
the previous value of i be restored, as the continuation’s tentacles reach
in to the consciousness of the program and find that moment in time?

Gavin