Currying with Procs

The word “curry” comes from the mathematician Haskell Curry. (I am resisting all attempts to make a lame joke about an Indian dish.) In functional programming, currying is the process of turning a function that takes n arguments into one that takes a single argument, but returns n functions that take one argument.

For example, given a lambda that accepts three parameters:

 >>​ discriminant = lambda { |a, b, c| b**2 - 4*a*c }
 
 >>​ discriminant.call(5, 6, 7)
 =>​ -104

you could convert it into this:

 >>​ discriminant = lambda { |a| lambda { |b| lambda { |c| b **2 - 4*a*c } } }
 
 >>​ discriminant.call(5).call(6).call(7)
 =>​ -104

In Ruby, there’s a shorter way to do this using Proc#curry:

 >>​ discriminant = lambda { |a, b, c| b**2 - 4*a*c }.curry
 
 >>​ discriminant.call(5).call(6).call(7)
 =>​ -104

Notice that using Proc#curry alleviates the need to have nested lambdas, as seen in the previous example.

Alright, so even Ruby’s creator thinks that you wouldn’t have much use for Proc#curry. But don’t let that dampen your learning spirit! Currying is very useful for creating new functions from existing ones. It gets more useful in functional languages (and languages such as Haskell use it to great effect), but you can see examples of this in Ruby:

 >> greeter = lambda ​do​ |greeting, salutation, name|
 >> ​"​​#{​greeting​}​​ ​​#{​salutation​}​​ ​​#{​name​}​​"
 >> ​end

In the preceding function, if you wanted to use greeter, you would have to supply all three arguments:

 >> greeter.call(​"Dear"​, ​"Mr."​, ​"Gorbachev"​)
 => ​"Dear Mr. Gorbachev"

What if you wanted to construct a greeter that always started with “Dear”? With a curried Proc or lambda, you very well can:

 >>​ greeter = lambda ​do​ |greeting, salutation, name|
 >>​ ​"​​#{​greeting​}​​ ​​#{​salutation​}​​ ​​#{​name​}​​"
 >>​ ​end
 
 >>​ dear_greeter = greeter.curry.call(​"Dear"​)
 =>​ ​#<Proc:0x007f902ba542f0 (lambda)>

With dear_greeter defined, you can use it like so:

 >>​ dear_greeter.call(​"Great"​).call(​"Leader"​)
 =>​ ​"Dear Great Leader"

Of course, if you find .call slightly verbose, you can always write it this way:

 >>​ dear_greeter.(​"Great"​).(​"Leader"​)
 =>​ ​"Dear Great Leader"

Here, dear_greeter is constructed from greeter by partially applying the first argument. Let’s see another example:

 sum_ints = lambda ​do​ |start, stop|
  (start..stop).inject { |sum, x| sum + x }
 end
 
 sum_of_squares = lambda ​do​ |start, stop|
  (start..stop).inject { |sum, x| sum + x*x }
 end
 
 sum_of_cubes = lambda ​do​ |start, stop|
  (start..stop).inject { |sum, x| sum + x*x*x }
 end

What do you notice? All three of the preceding lambdas have the same structure:

 sum = lambda ​do​ |start, stop|
  (start..stop).inject { |sum, x| sum + ​??​? }
 end

This suggests that some form of refactoring can occur. The only thing that is different is the portion marked as ???. It’s straightforward to extract out the common logic and make it an argument:

 sum = lambda ​do​ |fun, start, stop|
  (start..stop).inject { |sum, x| sum + fun.call(x) }
 end

So what does this buy you? Well, now you can make use of sum to build other methods:

 sum_of_ints = sum.(lambda { |x| x }, 1, 10)
 sum_of_squares = sum.(lambda { |x| x*x }, 1, 10)
 sum_of_cubes = sum.(lambda { |x| x*x*x }, 1, 10)

Of course, even doing it this way requires you to specify all the arguments up front. What if you wanted to calculate sum_of_squares but wanted to defer supplying the ranges? For example, a user has the option to select the range via a form. Here’s currying to the rescue:

 sum_of_squares = sum.curry.(lambda { |x| x*x })

Now, you can make use of sum_of_squares with any valid range you desire:

 sum_of_squares.(1).(10) => 385
 sum_of_squares.(50).(100) => 295475

That’s enough currying for now. It’s time to open that terminal and work that brain with some exercises.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.225.57.164