‘Yield’ in Ruby – a (not so) brief introduction


 

The ‘yield’ keyword in Ruby was something I really struggled to wrap my head around when it was first introduced to me. It took a fair bit of reading to finally click. Time well spent, I think, because getting a workable understanding of yield has by extension deepened my understanding of functions and blocks in the greater context of Ruby.

Put bluntly, a yield statement is used within a function to literally yield (stop) the execution of that function to a particular block of code. A block of code is anything contained within the parameters of a pair of curly brackets { } or the keywords do and end.

 

Consider the following lines of code:

Screen Shot 2018-10-09 at 20.02.07

This is the terminal output:

Screen Shot 2018-10-09 at 20.04.56.png

The first puts statement executes no problem. The second (line 4), however, has to wait for the block of code we’ve injected into the yield statement (within the curly brackets that follow the method call) to execute – whatever this may be. In this case it’s simply another puts statement, however this functionality can be utilised for far more useful things. I’ll get into these later.

Our previous yield statement is pretty useless. In its current state any variables we declare within the function aren’t accessible to the code ‘injected’ into the yield block (contained within the curly brackets). For example, the following code..

Screen Shot 2018-10-10 at 15.23.12.png

…will throw us this error:

Screen Shot 2018-10-10 at 15.25.52.png

This is because the variable (‘var’) is not accessible for the yield block to use – it is ‘scoped’ out of it. In order for us to be able to operate on it within the yield block, we need to pass it in as a parameter when calling on yield. Let’s modify the function in light of this:

Screen Shot 2018-10-10 at 15.27.25.png

Within the pipes (| |) we have a placeholder for the variable, which we then operate on within the rest of the block. The operation in this case is merely a puts statement (though it could be as complex as we desired) – and it successfully puts out our variable to the console! –

Screen Shot 2018-10-10 at 15.28.13.png


 

Unlike many other languages, Ruby has some extremely useful built-in methods – .each, .map and x.times do, for example. If we look under the hood, all of these methods depend on the yield keyword to function.

Take .each for example. This method iterates through each element of an array or hash and operates on them individually with a block of code. Simple enough.

But how does it do this? How does it know to stop executing only when each item in the array/hash has been operated on? How does it know which element of the array/hash to operate on in a given instance of block execution? The answer lies in the yield statement.

The code below, for example, will output the numbers 1 to 4 in the terminal:

Screen Shot 2018-10-09 at 20.27.06.png

Peeking under the hood, this is how yield is being used to create the .each method:

Screen Shot 2018-10-10 at 13.43.00.png

Let’s break the above code down:

  1. First, we set a counter i equal to 0.
  2. We then start a while loop that will keep going until the condition is no longer being satisfied – namely that the length of the parsed array is greater than the counter i.
  3. We then have the yield statement itself, into which is passed our array taken to the index of our counter ([i]).
  4. The next line of code adds 1 to the value of i, thus taking us to the next element in the array when we come back around to the yield statement the next time. This is the part that allows us to pass each individual element of the array into the yield statement as a parameter only once during the while loop.
  5. When the value of i becomes equal to the length of the array (4 elements long in this case), the condition allowing the while loop to run is no longer satisfied, and the loop is broken. Thus the block of code is finished once each element in the parsed array is operated on.

 

As someone who did some work in JavaScript before moving on to Ruby I thought it was interesting how this use of yield (that allows .each  to function) mirrors the for loop in JavaScript:

Screen Shot 2018-10-10 at 15.32.03.png

..personally I’m grateful a simple array iteration is as simple as .each in Ruby!

 


 

Aside from powering some highly useful built-in Ruby methods, yield can also be utilised in the formation of our own customisable methods. Take our previous method that currently iterates through an array and puts each element out to the console. What if we wanted to do something to each element before putting it out to the console – for example, adding some punctuation to the end of it? We could write another method to do this, but this isn’t very DRY of us, especially if we want to have the ability to print the elements with all sorts of different punctation. Instead, we can reuse our previous method and inject these modifications into the yield block..

Screen Shot 2018-10-10 at 15.34.18.png

..thus putting our desired output to the console without having to laboriously write a whole new method! –

Screen Shot 2018-10-10 at 15.35.53.png

What if we wanted a question mark instead? Easy:

Screen Shot 2018-10-10 at 15.36.28.png


 

Using yield, not only can we now efficiently retrofit functions to suit our needs, we can also build our own custom variations of methods like .each and .map with unique functionality. For example, by tweaking the value by which the counter i goes up each time the while loop repeats, we can iterate through every other element in the array:

Screen Shot 2018-10-10 at 15.38.50.png

In changing the iterator to i = i + 2, the method call will now print every odd number…

Screen Shot 2018-10-10 at 15.39.10.png

…but the scope for creative functionality is potentially endless.

Yield is a tricky concept. But taking the time to learn how it works allows us to understand what goes on every time we use a built in method like .each or .map, and how to modify the underlying logic of these methods with our own custom yield statement, if they aren’t quite granting us the specific functionality we need.


 

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 )

Google+ photo

You are commenting using your Google+ 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 )

Connecting to %s