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.)
Both the puts and 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 what10.timesmeans. 10 is an instance of theFixnumclass, which has atimesmethod. I’ll get into classes and objects in chapter 5. - The
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 thedokeyword means... that you're passing a block as an argument to a method. - Once YARV starts executing the
timesmethod, it creates a newrb_control_frame_tstructure. As Nadia said at one point, eachrb_control_frame_tstructure represents a level in your Ruby program’s call stack. So each time you call a method, you get a newrb_control_frame_tstructure and a new level in your Ruby program's call stack. Each time you return from a method, arb_control_frame_tstructure is deleted. - This
rb_control_frame_thas a type CFUNC because thetimesmethod was written by the Ruby team in C and not Ruby. What thetimesmethod 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 anotherrb_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:
https://github.com/ruby/ruby/blob/trunk/numeric.c#L4972
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.
Right! So what I said above, that "do" means you are passing a block as an argument, doesn't apply in for-loops or while-loops. One confusing detail about Ruby syntax I suppose.