UPDATE: Please note: Even if Rubyistas claim it, yield / block constructs are not coroutines
Intro
The following is a standard example in Ruby for the combination of code-blocks {... } and yield-statement:
#!/usr/bin/ruby
def test
puts "You are in the method"
yield 1
puts "You are again back to the method"
yield 2
end
test {|a| puts "You are in the block #{a}"}
Output
/usr/bin/ruby -w /home/lanx/perl/ruby/yield.rb
You are in the method
You are in the block 1
You are again back to the method
You are in the block 2
Goal
sub test {
say "You are in the method";
yield 1;
say "You are again back to the method";
yield 2;
}
test b{ say "You are in the block $_[0]" };
should produce the same results
Approach
Just to prove my strong opinion that Ruby is semantically just a Perl dialect I tried to simulate it with some syntactic sugar:
use strict;
use warnings;
use feature qw/say/;
=pod
Block sub is basically syntactic sugar for taking an anonymous sub {}
and returning it. Blessing it with "Block" facilitates syntax checks.
=cut
sub b(&) {
my $c_block=shift;
bless $c_block, "Block";
return $c_block;
}
=pod
"yield" uses a little know trick to access the @_ of the caller. It
checks the last argument of the caller, if its a ref blessed to
"Block" and calls it with the own arguments.
Note:
Ruby allows mixing normal arguments and blocks, as long as the
block is in the last position. When using & prototypes in Perl like
in C<test(&&)> only the first block can be passed w/o leading sub.
=cut
sub yield {
package DB;
my ($package, $filename, $line, $subroutine)=caller(1);
no warnings;
my $c_block = $DB::args[-1];
package main;
return $c_block->(@_)
if ref $c_block eq "Block";
die "$subroutine called w/o block in $filename line $line";
}
=pod
simulating the ruby test-code
=cut
sub test {
say "You are in the method";
yield 1;
say "You are again back to the method";
yield 2;
}
test b{ say "You are in the block $_[0]" };
Output
/usr/bin/perl -w /home/lanx/perl/ruby/yield.pl
You are in the method
You are in the block 1
You are again back to the method
You are in the block 2
Comments
Of course all of this comes with a performance penalty.
But it's worth noting that Ruby itself is considered much slower than Perl5.
An additional implementation in XS or as a new feature pragma would solve performance issues in a compatible way, w/o breaking compatibility
Main differences remaining to the original code are:
- Ruby tries to avoid curlies with do/end pairs:
block/end pairs could be inserted in Perl but I'm not sure if it's worth the trouble.
- No semicolons in Ruby, this could be (somehow) done with a source filter ... but again, worth the trouble?
- argument signatures are lengthy in Perl, something like my ($a,$b)=@_; takes >5 letters more than just |a,b|.
Curious for more comments...
Cheers Rolf
( addicted to the Perl Programming Language)
-
Are you posting in the right place? Check out Where do I post X? to know for sure.
-
Posts may use any of the Perl Monks Approved HTML tags. Currently these include the following:
<code> <a> <b> <big>
<blockquote> <br /> <dd>
<dl> <dt> <em> <font>
<h1> <h2> <h3> <h4>
<h5> <h6> <hr /> <i>
<li> <nbsp> <ol> <p>
<small> <strike> <strong>
<sub> <sup> <table>
<td> <th> <tr> <tt>
<u> <ul>
-
Snippets of code should be wrapped in
<code> tags not
<pre> tags. In fact, <pre>
tags should generally be avoided. If they must
be used, extreme care should be
taken to ensure that their contents do not
have long lines (<70 chars), in order to prevent
horizontal scrolling (and possible janitor
intervention).
-
Want more info? How to link
or How to display code and escape characters
are good places to start.