Anatomy of Ruby’s Built-in Methods: An Introductory Case Study Array#map

As a beginner, the use of built-in methods in Ruby has been a fundamental focus as I try to learn how to program effectively and clearly. It is important to understand how and why methods work the way they do. As such, I feel using Array#map will be useful in breaking down the steps in how a method works. Please keep in mind, in terms of my education, I haven't taken the Object Oriented Programming(OOP) module yet. I’ll be there soon enough, but for now, the details of OOP are foreign to me. I’ll return to the topic of Ruby’s built-in methods again once I have a solid grounding in OOP fundamentals.

TERMINOLOGY
First and foremost, there is some mandatory terminology associated with methods that needs to be defined. If you have no experience with programming, the easiest way to think of a method is a piece of code that takes input, evaluates the input and returns output based on its evaluation. This input and output can take many forms, but that will be discussed later. 
Visually, this would potentially look like

# Example one
[1,2,3,4].map do |arr|
arr*2
end

This example has three overarching parts that allow the method to work. 
The input or caller which is [1,2,3,4],
the form of [] is called an array,
the information inside the array is called an element (the above example has 4 elements),
the method which is .map,
the block which is everything in the above code from do to end.

So to apply context, [1,2,3,4] is an array of 4 elements. In the above example, this array is being used as a caller for a method.

What Array#map does is create a new array based on the what the block evaluates and mutates from the original array. In the above example it takes each element of the array (which is to say 1 and 2 and 3 and 4) then it multiples them by two. It takes the result of that evaluated statement (the return value) and puts it in the same spot (Index point) in the new array as it was in the old array. So 1 would be multiplied by 2, the result is 2. This return value would be placed in the same spot in the new array. In Ruby we call this process Iteration. To clarify, iteration is when we take a list or collection, such as an array, and repeat the methods function over each element.

PROVIDING A METHOD WITH INPUT
In ruby, when we provide a method with a caller we refer to it as a method call or calling the method. Figuratively, this means we are “calling” the method to ask it to perform its function/evaluate an caller. Contextually, if you are asked to explain some code you wrote you might say something along the lines of “I needed to create a different array with data based on my old array. So I wrote a method call on the old array.” Another concept to note is that not all methods require input. In this case, when we create a method call such as

# example one
[1,2,3,4].map do |arr|
arr*2
end

the input of the caller is required. There are types of methods that do not need input. We aren’t discussing those here, but it is an important idea to note.

USING BLOCKS TO EVALUATE AND/OR MODIFY CALLERS
Before we get into blocks proper, it is important to mention that blocks and methods are separate concepts. They are not the same thing. Methods may use blocks but aren't always required to do so. Blocks however, cannot work without a method. Keep this in mind as we talk about how blocks and methods interact.
At this point,in order to provide a useful return value, the method might need a block to evaluate the given caller. Array#map is one of these methods.

# Example one
[1,2,3,4].map do |arr|
arr*2
end

If you recall from earlier, the block is everything from the do to the end. This is commonly referred to as a do end block. Another example of a block would be

# Example two
[1,2,3,4].map {|arr| arr*2}

This is the same as example one, except for the syntax in how it is written. When deciding on which block format to use the general consensus is that if the block has multiple lines use a do end block. If the block is a single line, use the curly bracket format. However, the most important aspect is to make sure the code is readable. Single line code is only useful if you can still understand it. 
To help provide visual clarification to the above statement, in the above two examples the block sections of the code are in bold.

# example one
[1,2,3,4].map do |arr|
arr*2
end
# example two
[1,2,3,4].map {|arr| arr*2}

In terms of how a block works, it relies on the method to pass it data to evaluate. This passing of a object reference is done between the | | section of the code. This is called a block argument. In between the || is a variable that holds whatever reference the method assigns to it. Again while it is a variable, we refer to it as a block argument. In example one it is the |arr| that allows the block to access the references. In example one, Array#map would assign each element, in order, to |arr| to evaluate its object. |arr| would hold the element assignment until a new element is passed to it. 
After the block argument, comes the actual evaluation and/or modification of the object. In example one, the first element to be evaluated would be 1. The object reference of 1 would be assigned to |arr| so the block could use the object. Then the block is told to take arr and multiply it by 2. The results, or return value, is implicitly returned to the new array. This is why there is no code saying “return the results of this evaluation to the new array”. Ruby does this automatically to the last evaluated statement. In example one we only have one statement to evaluate:

arr*2

Therefore, once it evaluates the above statement. It gives the return value to Array#map to place in the new array. It is important to note, in this example, the block does not take the action of placing the return values in the new array. Array#map starts and ends the program of example one. The block is responsible for taking the object reference from the method, evaluating it and providing a return value, nothing else. Other methods modify the original caller using the return values of the block, Array#map does not do this but Array#map! does. Be aware of small differences like this.

UNDERSTANDING RETURN VALUES
If you are coming from a background outside of programming, return values can be an odd concept. First and foremost it is important to note that the return value is not necessarily the output that gets shown on your computer. When a method is called, it has to return something. What this ‘something’ is depends on the method. However, this ‘something’ that the method returns is the return value. With map, this ‘something’ is a new array. The following examples will help clarify

# Example one
[1,2,3,4].map do |arr|
arr*2
end

In example one we see that the block is being told to take the value in arr and to multiply it by 2 . Using addition, we know these values will result in 2 4 6 and 8 . If you assume that these values are returned to Array#map to be placed in the new array, you would be correct. This is not expressed visually in Ruby. Instead you have to learn, through experience and knowledge, when a value is being implicitly returned. However the value returned is not always the same as what you would expect.

# example three
[1,2,3,4].map do |arr|
puts arr*2
end
# => 2,4,6,8
# => [nil,nil,nil,nil]

There are two different results in this example. The first result is 2 4 6and 8 . This is the output that gets printed to your computer screen. These are not return values. Instead the return values are given to the new array. Earlier we clarified how Array#map works on a simplified level. We know it takes the return values and places them in the correct index points . We also know that we must get a new array at the end of the method call. Using this information we can come to the conclusion that the code above is doing two things in regards to output.

First, it is using the puts method(yes, puts is a method*) and telling it, “Take the total of arr*2 and display it on the screen.” This is simple to understand. puts displays the results of an evaluation. It does this with every iteration
* puts can also be written puts(obj) which helps clarify that it is a method. for example, puts('hi') # => 'hi' is the same as puts 'hi' # => 'hi'

Real quick, it is important to note that puts is the last evaluated method in the block. Similarly, it is important to know that puts doesn't just display the results of an evaluation. As we stated earlier in this section, When a method is called, it has to return something. puts returns an object and this object is nil . nil basically just means that there is no value. Its a way for Ruby to say, “hey, this object has no value”.

So back to the example. The second thing the block does in regards to output, is check the return value of puts . Remember, puts is the last evaluated method in the block. Therefore, its return value is given to Array#map . This value is nil . Therefore, nil is repeatedly placed in the new array until the method call is ended. A explicit visual example of what’s going on with 
 puts arr * 2would look like puts(arr.*(2)) . As you can see, the puts will be the last method in this grouping to give its return value. All the other methods must complete before puts can evaluate. As a result, its return value is what is ultimately given to map .

Return values can be a very confusing. Just remember, if you are using a method, it must return a return value. You might notice I didn't mention the return value of one of the methods used in my examples. map is also a method, so it must have a return value as well! This return value is the new array [2,4,6,8] . Literally, the return value of map is simply a new array. However in example one, we placed the return values of the block in the new array which was the return value of map .

ENDING A METHOD CALL
Once the block has evaluated all available block arguments, its job is done. At this point we return the the Array#map method. A few things happen at this point. Array#map has its own return value, which is the new array! Example one results in the following

# example one
[1,2,3,4].map do |arr|
arr*2
end
# => [2,4,6,8]

Note that it provides a new array. The old array remains unmodified. For example if you were to assign the original array to a variable, you would receive the following results:

# example four
array_one = [1,2,3,4]
array_one.map do |arr|
arr*2
end
# => [2,4,6,8]
array_one
# => [1,2,3,4]

This is important as it shows that we are not modifying the original caller. Instead we are using it almost as a template or reference to work from. Again, there are methods that modify the original caller. It is important to know when you are using them, and to think carefully about if permanent mutation is needed. 
Once the method provides its return value, the method call has ended. This return value can be assigned to a variable for later use. If the original caller was permanently mutated, the return value will be the mutated original caller.

Conclusion
I hope this breakdown has been helpful to anyone trying to get a grasp of Ruby at a beginner level. A friend of mine, who is a programmer, told me that there are not enough people at step two helping people at step one. I’m not sure if I’m at step two but I wanted to provide some info for those people who get confused by some of the concepts out there. I tried to include the parts that I got stuck on while learning, and clarify them in writing and with visual aids. I especially hope this helps people who don't have a STEM background. Coming from a humanities background (or a no higher education background) requires a different type of thinking and logical model. I hope this breakdown helps bridge that gap a little bit. Good luck and happy programming!