Callcc and insomnia

Hello,

This summer I’ve been delving a lot more into various programming
languages that I had previously wanted to learn but didn’t really have
time. So, I’ve been studying my Ruby, Scheme, Smalltalk and Haskell
lately, reading a lot of newsgroups and such. So, of course, I came
across the idea of closures, continuations and coroutines
(specifically reading the parrot blog) and became intrigued by them. I
hadn’t heard of them previously being that I came from C/C++ and Java
mostly. I wanted to implement coroutines (Perl 6 style, as described
in the blog**) myself since most languages don’t have those. I tried
in Scheme first but became very confused as the syntax is very foreign
to me still, so I decided to switch to Ruby (which I like better
anyhow) before I tried again in Scheme.

And, I’ll add, that I succeeded, as this code shows:

class Coroutine
def initialize(method)
@body = method
end

def call(*args)
	callcc do |@jump_out|
		if @jump_in.nil?
			ret = @body.call(*args) do | val |
				callcc do | @jump_in |
					@jump_out.call val
				end
			end
			
			@jump_in = nil
			@jump_out.call ret
		else
			@jump_in.call
		end
	end
end

end

Which lets you write code like this:

def func(a, b, c)
yield a
yield b
c
end

cot = Coroutine.new method(:func)

puts cot.call(“mook”, “mock”, “morn”) # => mook
puts cot.call(“mush”, “milf”, “maern”) # => mock
puts cot.call(“mush”, “milf”, “maern”) # => morn
puts cot.call(“mush”, “milf”, “maern”) # => mush
puts cot.call(“mush”, “milf”, “maern”) # => milf
puts cot.call(“mook”, “mock”, “morn”) # => maern

Which I was pleased with.

Now for my question:

When I first wrote it, intstead of:

ret = @body.call ...
    ...
end

@jump_in = nil
@jump_out.call ret

I simply let @body.call return which should (I thought) return from
the callcc with an appropriate value. This meant that each time I had
to do something that looked like:

loc, @jump_in = @jump_in, nil
loc.call

so that I didn’t get in an infinite loop.

However, the output for 3 calls would be as follows:

val1
val2
val3
val1
val2

So func got called extra times.

So, maybe some of the gurus out there can help me out and tell me
what’s different about the code that works and the code that I thought
was semantically the same but doesn’t work in practice. I’ve gone
through the flow in my mind over and over but I can’t figure it out,
at least not this late.

Thanks for any help

Dan

** P.S.: By Perl 6 style coroutines, I mean routines that look as
follows (I may have syntax wrong as I don’t really know perl well)

sub foo is coroutine {
yield "a"
yield "b"
yield “c”
}

Where each “yield” is effectively a return with support for resuming
afterwards. All coroutine implementations I’ve seen (Scheme or Ruby,
that is) aren’t like this at all. They’re more like co-operative
threads that can switch context amongst one another, instead of
functions that can return and resume (as the parrot blog suggests). Is
the definition in the blog not the standard definition of coroutines?
From what I’ve read, what I’ve made seems more like a Python
generator. Is a generator a special case of a coroutine? Or is it the
other way around? Or maybe they’re the same or totally unrelated?

Maybe I’m just confused. :slight_smile:

Sorry for the extremely long post.

Dan, it seems that by calling the “call” method you do two things:

  1. push more input values into the coroutine and
  2. get the next return value.
    Is this what you wanted to achieve? Wouldn’t it be better to separate
    those two things?

Just interested,
Pit

···

On 22 Jul 2003 at 15:29, Dan Doel wrote:

class Coroutine
(…)
end

Which lets you write code like this:

def func(a, b, c)
yield a
yield b
c
end

cot = Coroutine.new method(:func)

puts cot.call(“mook”, “mock”, “morn”) # => mook
puts cot.call(“mush”, “milf”, “maern”) # => mock
puts cot.call(“mush”, “milf”, “maern”) # => morn
puts cot.call(“mush”, “milf”, “maern”) # => mush
puts cot.call(“mush”, “milf”, “maern”) # => milf
puts cot.call(“mook”, “mock”, “morn”) # => maern

Which I was pleased with.

Dan, it seems that by calling the “call” method you do two things:

  1. push more input values into the coroutine and
  2. get the next return value.
    Is this what you wanted to achieve? Wouldn’t it be better to separate
    those two things?

Just interested,
Pit

I guess it depends on how you prefer things.

If you look here: http://www.sidhe.org/~dan/blog/archives/000178.html
which is the place that I took my coroutine syntax/semantics from, it
has a discussion of all the ways you can pass arguments to a
coroutine. For those that don’t care to read the whole article,
there’s 3 ways:

  1. When the coroutine’s first called, you take those arguments and use
    them until the coroutine finishes and take new arguments at the next
    start
  2. Start the coroutine over when called with a new set of arguments
  3. Replace arguments with the new arguments but don’t restart at the
    beginning.

My code actually only does number 1. If you look, the first time in,
it calls the function with the arguments, and then until the function
falls off the end, it just jumps back in where it left off, without
changing the arguments. It wouldn’t be hard to implement 2 (store the
args in something like @args and compare before deciding whether to
jump or call), and, to my knowledge, 3 can’t be done unless you
require that people do something like:

def foo(x, y, z)

x, y, z = yield val1

x, y, z = yield val2

end

cot = Coroutine.new method(:foo) …

In which case the person who defines the corouting gets to choose
between 1 and 3 (although I think 3 is generally not seen to be good).
To that end you could make routines that expect a different number of
arguments each time they are called, which would be a little weird and
I can’t think of any time you’d use it, but you could do it!

Unfortunately, I don’t think there’s any ‘elegant’ way of letting
people choose 2 while still allowing 1 and 3, although 3 facilitates a
user implementation of 2:

def foo(x, y, z)
cc2 = nil
x, y, z = callcc { |cc| cc2 = cc ; x, y, z }

a, b, c = yield val1
cc2.call(a, b, c) unless (x == a) && (y == b) && (z == c)

end

(Might not work exactly as I think, since I just wrote that up on the
spot and haven’t tested it)

That’s kind of ugly code, though.

Anyway, I think I’ve forgotten to answer your question. The code does
what I intended. :slight_smile: If you wanted a Perl 6 coroutine (as I understand
them), you could do something like this (don’t yell at me for global
variables :), you can use class or instance variables at your
leisure):

$cot = nil

def coroutine_impl(xx, yy, zz)

end

def coroutine(x, y, z)
if($cot == nil)
$cot = Coroutine.new method(:coroutine_impl)
end

$cot.call(x, y, z)

end

And then you just call

coroutine x, y, z

to your hearts content and it looks like a transparent function call
just like a Perl 6 coroutine.

Not that this has a whole lot of benefit for the work involved, but I
did it more to help myself understand callcc than to actually have
something useful.

I’ve since thought about the Scheme implementation of such a beast,
but I still can’t wrap my head around it exactly. Then again, it’s
different than the Ruby implementation because you need to pass in a
return function which isn’t exactly usual in scheme (there isn’t
exactly a ‘yeild’ that does what it does in Ruby).

Hope this clears some stuff up, and sorry again for the long post.

I wondered if you might benefit from a trace
(err … like I did). I haven’t had time to
study your code in detail, but have saved it
in my ‘What_The_’ folder for a rainy day. :wink:

set_trace_func proc { | ev, fi, li, ty, bi, cl |
next if ev == ‘c-return’
printf("\n line:%-2d %15s %15s %8s … ", li, ty, cl, ev)
}

Add the lines after your defs and before the
stuff you want to follow. It’s quite easy
to filter out anything you don’t want to see.

daz

···

“Dan Doel” dolio@po.cwru.edu wrote:

Not that this has a whole lot of benefit for the work involved, but I
did it more to help myself understand callcc than to actually have
something useful.