Hi guys, So in episode 7 you were asking about where and how the
puts method connects to the computer’s display and writes the output. And in episode 8 you were asking about the
times method, how that worked and why it wasn’t implemented with YARV instructions. In both cases, I didn’t go into detail about this in the book because it would have distracted you from the topic at hand, which is how YARV executes your code. (Or in this case, my example Ruby code from the book.)
times methods were written by the Ruby code team, and not by you or me. Both of them are implemented in C code. So when you’re writing a Ruby program, some of the methods you write yourself, but you get many other methods for free because they are part of the Ruby language. Many of the these built in methods are part of the standard library, which means they are Ruby code written by the Ruby core team, while other built in methods are written directly in C code by the Ruby team.
As you know, the
puts method takes a string argument and writes it to the console, returning the value
nil. This actual work is performed by C code inside of Ruby which I don't show in the book. The YARV instructions, which your Ruby is converted into, simply calls this C code at the proper time. This is what the
send YARV instruction does.
send means to call a method. Later in Chapter 4 I get into the details of what the
send instruction actually does, and what it really means to call a method. So the YARV instructions don't write anything to the display; the C code for the
puts method does that.
To address your questions in episode 8 about how the
times method works, and what the
rb_control_frame_t structures are, let’s walk through this example again:
10.times do puts "The quick brown fox jumps over the lazy dog." end
- So first, we start with the number 10.
- Then, we call the
timesmethod on it. That’s what
10.timesmeans. 10 is an instance of the
Fixnumclass, which has a
timesmethod. I’ll get into classes and objects in chapter 5.
timesmethod takes a single block as a parameter. In Ruby methods can take many normal arguments, and optionally they can take one block as an argument as well. That's what the
dokeyword means... that you're passing a block as an argument to a method.
- Once YARV starts executing the
timesmethod, it creates a new
rb_control_frame_tstructure. As Nadia said at one point, each
rb_control_frame_tstructure represents a level in your Ruby program’s call stack. So each time you call a method, you get a new
rb_control_frame_tstructure and a new level in your Ruby program's call stack. Each time you return from a method, a
rb_control_frame_tstructure is deleted.
rb_control_frame_thas a type CFUNC because the
timesmethod was written by the Ruby team in C and not Ruby. What the
timesmethod does is loop from 0 up to the given number minus one (so from zero to nine inclusive), calling the block each time.
- Now the
timesmethod calls the block, and so we get yet another
rb_control_frame_tstructure. This one has a type BLOCK because it’s a block.
- Now the block code runs, which is comprised of YARV instructions, because I wrote it (the “puts the quick brown fox…” line).
If you’re brave and also curious, you can see the C code for the
times method here:
Basically, this is the C code equivalent of this Ruby code:
for n in 0..9 do yield n end
It loops from 0 to 9 (because I called
times on the object 10), and calls
yield n each time around the loop.
yield calls the given block (the block passed into the current method as an argument) and passes the given arguments to the block.
(It's a bit more complicated that that, because if you don't pass in a block
times returns an enumerator instead. Also the C code has a special case when the receiver is not a Fixnum object.)
A block is really just a special kind of method - one that, as you'll see later in Chapter 3, can access values in the parent or calling scope.