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.
JavaScript
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);
4.
5. // First function that takes 3 arguments.
6. const innerFunctionA = (a, b, c) => (a * a + 2 * b + c);
7.
8. // Second function that takes 3 arguments.
9. const innerFunctionB = (a, b, c) => (a * b + c);
10.
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
13.
14. // Calling the outer function with the second function that takes 3 arguments
15. const outerResultB = outerFunction(innerFunctionB); // (10 * 8 + 3) * 15 => 1245
16.
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;
2.
3. const result = add(5)(8); // 5 + 8 = 13
4.
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.
Ruby
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. inner_function.call(10, 8 , 3) * 15
3. end
4.
5. def inner_function_a(a, b, c)
6. a * a + 2 * b + c
7. end
8.
9. def inner_function_b(a, b, c)
10. a * b + c
11. end
12.
13. outer_result_a = outer_function(method(:inner_function_a))
14.
15. outer_result_b = outer_function(method(:inner_function_b))
16.
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:
- 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. - 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 ofmethod
toouter_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
end
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
4.
5. outer_result_a = outer_function {|a, b, c| a * a + 2 * b + c}
6.
7. outer_result_b = outer_function {|a, b, c| a * b + c}
8.
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
4.
5. result = add(5).(8)
6.
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.