Functions as Arguments and Return Values: JavaScript vs Ruby

Posted by Panos M. on 18/Jan/2018 (22:29)

Functions as Arguments and Return Values: JavaScript vs Ruby

JavaScript and Ruby are extremely powerful languages with dynamic features that you don't easily find in other programming languages. In this blog post, I demonstrate how one can work with functions as arguments and as return values of other functions.


Function as Argument in JavaScript

In JavaScript, it is very popular to define functions that take as arguments other function definitions. Look at the following example:

 1. // Defining a function that requires an argument
 2. // to be a function that takes 3 arguments.
 3. const outerFunction = (innerFunction) => (innerFunction(10, 8 , 3) * 15);
 5. // First function that takes 3 arguments.
 6. const innerFunctionA = (a, b, c) => (a * a + 2 * b + c);
 8. // Second function that takes 3 arguments.
 9. const innerFunctionB = (a, b, c) => (a * b + c);
11. // Calling the outer function with the first function that takes 3 arguments
12. const outerResultA = outerFunction(innerFunctionA); // (10 * 10 + 2 * 8 + 3) * 15 => 1785
14. // Calling the outer function with the second function that takes 3 arguments
15. const outerResultB = outerFunction(innerFunctionB); // (10 * 8 + 3) * 15 => 1245
17. console.log(outerResultA); // => 1785
18. console.log(outerResultB); // => 1245

The outerFunction takes as argument a function that is required to accept 3 arguments. You can tell that from line 3, in which outerFunction is using its function argument, i.e. innerFunction, by calling it and giving it the 3 arguments 10, 8, 3. Also, the innerFunction needs to be a function that returns something that can be multiplied by 15.

Then, on lines 5 and 9, we define 2 different functions that they both take as arguments 3 integers. Their implementation is different, but their signature is the same.

Finally, on lines 12 and 15, we call the outerFunction giving each of the 2 other 3-argument functions as its real argument. The comments explain also the results that we are expecting to get while running this JavaScript program.

Function as Return Value in JavaScript

Besides the case in which we use functions as arguments, we can also use functions as return values of another function. Here is an example that demonstrates this:

1. const add = (integer) => (anotherInteger) => integer + anotherInteger;
3. const result = add(5)(8); // 5 + 8 = 13
5. console.log(result); // => 13

The above JavaScript program defines a function (add), that takes as argument an integer, called integer and returns back as result a function ((anotherInteger) => ...), which takes as argument an integer too and returns back the result of adding the original integer to its own argument.

This example demonstrates how a function can return another function. Also, how the inner function has access to the arguments passed to the outer function.


But how can we do similar things in Ruby? Is it possible to pass a function as an argument? Is it possible for a piece of code that has a function handle to use it to call the function corresponding to that handle? Is it possible to return a function definition as a result of calling a function? Can inner functions have access to the arguments of the outer functions?

The answer is yes, to all these questions. Let's see some examples:

Function as Argument in Ruby

Here is the Ruby equivalent of the first example code that we have written:

 1. def outer_function(inner_function)
 2., 8 , 3) * 15
 3. end
 5. def inner_function_a(a, b, c)
 6.   a * a + 2 * b + c
 7. end
 9. def inner_function_b(a, b, c)
10.   a * b + c
11. end
13. outer_result_a = outer_function(method(:inner_function_a))
15. outer_result_b = outer_function(method(:inner_function_b))
17. puts outer_result_a # => 1785
18. puts outer_result_b # => 1245

If you run this you will get the same result (1785 and 1245) like with the JavaScript version. Watch out for the following tools that we used in order to have this implemented as required:

  1. The outer_function is calling the function corresponding to its argument with the help of the .call() method. This is the API exposed by Method instances.
  2. Any function definition can be converted to a Method instance if we pass its name to method call. This is what we do on lines 13 and 15, before sending the result of method to outer_function(...).

The above two steps are enough to make sure that outer_function can call the argument that is passed to it.

Note that explicitly calling .call(...) is not necessary for objects that respond to the .call(...) method. You can just do .(...):

def outer_function(inner_function)
  inner_function.(10, 8 , 3) * 15

Functions as Blocks

Besides the technique that we used above, a more popular technique used in Ruby, for an outer function to call a function that is passed as argument is the technique of blocks.

Here is the the previous example implemented using yield and blocks:

 1. def outer_function
 2.   yield(10, 8 , 3) * 15
 3. end
 5. outer_result_a = outer_function {|a, b, c| a * a + 2 * b + c}
 7. outer_result_b = outer_function {|a, b, c| a * b + c}
 9. puts outer_result_a # => 1785
10. puts outer_result_b # => 1245

Here, the outer_function is using yield which invokes any block argument given to outer_function when outer_function is invoked. On line 5, we invoke outer_function with a block argument which is equivalent to the inner_function_a that we had in the previous Ruby example. On line 6, we invoke outer_function with a block argument which is equivalent to the inner_function_b.

So, the final result is the same (1785 and 1245).

This technique is good as long as you have only one function to send as argument to another one. However, if you have more than one function argument, then this technique cannot be used.

Function as Return Value in Ruby

And how can we have a function being returned as a return value of another function in Ruby?

Let's implement the JavaScript example presented earlier, to its Ruby version:

1. def add(integer)
2.   -> (anotherInteger) { integer + anotherInteger }
3. end
5. result = add(5).(8)
7. puts result # 13

The add is now defined to return a lambda, which responds to .call(). Also, the lambda returned takes as argument another argument (anotherInteger) and has access to the local variables the lambda lives in. Hence, it can have access to integer, which is the argument given in add.

Closing Note

JavaScript and Ruby are my favourite programming languages, because they are very powerful and have been proven invaluable tools in my Web Development career.

Please share your thoughts in the comments section below. I know that there are plenty of other ways to do the same things in JavaScript and in Ruby. Let me know, as I always learn a lot from you.

Finally, I want to mention here that on our Full Stack Web Developer course we teach both Ruby and JavaScript. This is a Mentor supported course that you pay-as-you-go. Your Mentor is assigned to you and evaluates your work and your progress, making sure that you improve on every step that you take.