Home > Uncategorized > Ruby’s yield

Ruby’s yield

One of the great things in C# is yield return. It’s a form of continuation implemented atop the CLR in an ingeniously lightweight way, purely by transforming the body of an ordinary method into a state machine class.

The parameters and local variables become fields of the class, allowing it to run in stages:

IEnumerable<int> Magic()
{
    yield return 0;
    yield return 1;
    yield return 2;
}

The caller of this method doesn’t immediately cause it to run. Instead they get back an object from which they can request values one at a time (or in C++ parlance, a const forward iterator), thus causing the statements in the method to run on demand, stopping at each yield return.

foreach (var n in Magic())
    Console.WriteLine(n);

On first glance at Ruby it appears to have the same thing built in. Indeed, some tutorials on Ruby strongly imply that the same magic is at work here:

def Magic
    yield 0
    yield 1
    yield 2
end

We call it as follows:

Magic { |n| puts n }

The overall effect is the same: by the time we leave the calling statement, the whole of the Magic method has finished and the block has run three times, much like the body of foreach in the C# version.

But despite the similar keywords, it’s not the same at all. The Ruby version rendered in C# would look more like this:

void Magic(Action<int> yield)
{
    yield(0);
    yield(1);
    yield(2);
}

With calling code like this:

Magic(n => Console.WriteLine(n));

The Ruby yield keyword means that the containing method requires something function-like to be passed to it, and yield just calls that function-like thing. This is a hell of a lot simpler than our original C# version.

In fact it’s so simple, you could do it in plain old C!

void magic(void (*yield)(int))
{
    yield(0);
    yield(1);
    yield(2);
}

And to call it, we have to put the block in a named function: C is a lame language with no anonymous functions.

void __block__(int n)
{
    printf("%d\n", n);
}

And finally:

magic(__block__);

So is Ruby a weak and feeble language? Far from it. As a dynamic language, it can do things with its own execution engine that would make a C# compiler blush. It has callcc, same as Scheme’s call-with-current-continuation. On top of that, the equivalent of C#’s yield return can be implemented purely by a library: the Generator class.

So in C# (and functional languages) we have lazy lists, and we can chain together operations on them, like Select (map), Where (filter), Aggregate (fold). What’s the equivalent in Ruby? I haven’t completely figured that out yet. I’m still reading my Ruby book.

Advertisements
Categories: Uncategorized Tags: , ,
  1. March 8, 2010 at 8:29 pm

    I finished my first Ruby book last month.

    One place to find things like Select() and Where() will be unsurprising from the language constructs that contain them. In C# they’re extension methods declared on the Enumerable type; they can apply to any implementor of IEnumerable. In Ruby they are part of the Enumerable mixin and thus apply to any object that mixes in the mixin.

    I’m just starting to ponder Ruby, but the mixin is one of my favorite things so far. I’m thinking of trying to build a scaffolding library that could simulate some of its powers in C#, probably by codegen into a partial class. I haven’t even googled the idea yet, and it’s rather obvious, so perhaps there are already dozens such blogs out there already, if not actual project. [google, google] Yep. http://code.logos.com/blog/2009/04/creating_mixins_with_t4_in_visual_studio.html

    -Mark

    • earwicker
      March 16, 2010 at 12:06 pm

      Yes I’ve wondered about doing the same kind of thing, where a mixin is any ordinary class M that implements an interface I. When mixed into a new class C, that causes C to implement I (so you get a kind of multiple inheritance). Also – very important for mixins – where M has a virtual method, that can be overridden in C. The best way I could think of was to do code gen to create a special base-class for C to be derived from, such as C_Mixins. That way you can implement the overridable virtual/abstract method support.

  1. October 31, 2013 at 11:37 pm

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: