DEV Community

loading...

Functions Creating Functions! (Ruby edition)

Daniel Hintz
Junior full-stack developer with focus in JavaScript (ES6) and Ruby. Experience with React, Redux, Rails, among others.
・3 min read

A couple weeks ago, I wrote a blog about building function-building functions in JavaScript. This week, I'm going to go through the same concept using Ruby. I'm going to stick as closely as possible to the same flow as the last article to make it easy to compare.

We all know what a programming function is and what it does. It encapsulates a particular behavior. For example, this function divides any number you'd like by 5.

def divide_by_5(number)
    number / 5
end

divide_by_5(15)
  # => 3
Enter fullscreen mode Exit fullscreen mode

In life, we often see different variations of a complex behavior, and this is a situation we see a lot in programming as well. For example, imagine we wanted to add some complexity to the above function so that it only divides numbers that are cleanly divisible by 5. We could easily do this:

def divide_evenly_by_5(num)
    if num % 5 === 0
        return num / 5
    end
    return "#{num} is not divisible by 5!"
end

divide_evenly_by_5(15)
  # => 3
divide_evenly_by_5(7)
  # => "7 is not divisible by 5!"
Enter fullscreen mode Exit fullscreen mode

But we might need to similarly divide by other numbers later on in our program. We could write a new function for each number, but that would be a pain. Instead, let's create a function which in turn creates other functions!

To do this, we'll convert our function into something called a lambda function, which is basically a way to encapsulate a certain behavior into a variable, very similar to const myFunction = function(arg) { console.log("hi "+ arg) }. We need to use .call() to call this lambda function:

divide_evenly_by_5 = ->(numerator) { 
    if numerator % 5 === 0
        return numerator / 5
    end
    return "#{numerator} is not divisible by 5!"
}

divide_evenly_by_5.call(15)
  # => 3
divide_evenly_by_5.call(7)
  # => "7 is not divisible by 5!"
Enter fullscreen mode Exit fullscreen mode

But this is too strict! Let's loosen up the lamda so that we can use whatever numerator AND denominator that we want. This is how we set up our flexibility in Ruby, rather than the wrapper function that we used in JavaScript. It's basically the same thing that we did in the JavaScript tutorial, but in reverse. (Instead of building a super-function out of 2 smaller functions, we're building a super-function and then breaking it into 2 smaller functions.) Note that the order of your arguments are important here:

divide_by = ->(divisor, numerator) { 
    if numerator % divisor === 0
        return numerator / divisor
    end
    return "#{numerator} is not divisible by #{divisor}!"
}

divide_by.call(5, 15)
  # => 3
divide_by.call(5, 7)
  # => "7 is not divisible by 5!"
Enter fullscreen mode Exit fullscreen mode

Now, we need to use a concept called currying to essentially split this method up into 2 "parts." and in Ruby we use a .curry method to do it. This is going to represent whatever the first divide_by argument is, which is why it's important to order them correctly:

divide_by_5 = divide_by.curry.call(5)
Enter fullscreen mode Exit fullscreen mode

Now, we can call divide_by_5 and provide the second argument, in this case the numerator:

divide_by_5.call(10)
  # => 2
Enter fullscreen mode Exit fullscreen mode

The benefit of this pattern is that we can now assign this behavior to any divisor/number variation. As the complexity of the behavior goes up, this becomes more and more useful.

Here's the full code:

divide_by = ->(divisor, numerator) { 
    if numerator % divisor === 0
        numerator / divisor
    end
    return "#{numerator} is not divisible by #{divisor}!"
}

divide_by_5 = divide_by.curry.call(5)
  # => 
div_by_8 = divide_by.curry.call(8)
  # => 
divide_number_by_100 = divide_by.curry.call(100)
  # => 

divide_by_5.call(15)
  # => 3

divide_by_5.call(8)
  # => "8 is not divisible by 5!"

divide_number_by_100.call(500)
  # => 5
Enter fullscreen mode Exit fullscreen mode

Disclaimer: There is a LOT more to these lambdas than this application, they're super useful in Ruby coding. I'm just getting started diving into their functionality.

Discussion (0)