Currying

Posted over 2 years ago in Higher-Order Ruby.

Note: This is the sixth article in my Higher-Order Ruby series. The previous articles can be found at the following links:

  1. Recursion and Callbacks
  2. Dispatch Tables
  3. Caching and Memoization
  4. Iterators
  5. Infinite Streams

All the examples in this chapter are trivially translated (switch sub { ... } to lambda { ... }). Ironically, I have never seen a chunk of idiomatic Ruby do anything like this. Rubyists clearly favor blocks for this sort of work. Have a look at the stream addition and multiplication examples of this chapter, for example. You can also see this when MJD trying to create a suitable inject() for Perl (he calls it reduce/fold).

Another interesting point about this chapter is how much of it is spent warring with Perl's syntax. MJD really struggles to introduce a block-like syntax for curried methods and is outright defeated in a couple of attempts. I really like how easily Ruby yields to our attempts to reprogram her, in sharp contrast to her sister language.

Continuing that line of thought, here's my best effort at the Poor Man's Currying library:

#!/usr/bin/env ruby -w

class Proc
  def curry(&args_munger)
    lambda { |*args| call(*args_munger[args]) }
  end
end

class Object
  def curry(new_name, old_name, &args_munger)
    ([Class, Module].include?(self.class) ? self : self.class).class_eval do
      define_method(new_name) { |*args| send(old_name, *args_munger[args]) }
    end
  end
end

Unlike the Perl, this feels like very natural Ruby to me. You could argue that I struggle with Ruby's syntax because it's not easy to curry a block-taking method, but that seems like a minor issue. (This is fixed in Ruby 1.9.)

Here are examples of me playing with my toy:

#!/usr/bin/env ruby -w

require "curry"

multiply = lambda { |l, r| l * r }
double   = multiply.curry { |args| args + [2] }
triple   = multiply.curry { |args| args << 3 }

multiply[5, 2]    # => 10
double[5]         # => 10
triple[5]         # => 15
triple["Howdy "]  # => "Howdy Howdy Howdy "

class Value
  def initialize(value)
    @value = value
  end

  def *(other)
    @value * other
  end
  curry(:double, :*) { [2] }
  curry(:triple, :*) { |args| args.unshift(3) }
end

five, howdy = Value.new(5), Value.new("Howdy ")
five * 2      # => 10
five.double   # => 10
five.triple   # => 15
howdy.triple  # => "Howdy Howdy Howdy "

I am purposefully trying to show the library's flexibility in these calls. The version in the book only supports currying a single argument at the front of the list. In 15 lines the Ruby version can completely rewrite arguments as it sees fit and it includes OO support.

You be the final judge, but I stand by my claim that Higher-Order Perl is the quest to bring many Rubyisms to Perl.

If you want to go deeper down the rabbit hole of currying in Ruby, I recommend you check out the Ruby library Murray. If you want to see a more Rubyish example of the FlatDB MJD builds in these pages, also see my article on code blocks.

Gregory added 9 months later:

The code is super clean, though i did need to get into 'functional programming mode' to get it (took me about 5 minutes to understand what was going on).

Currying is academically super interesting, but I think it's more useful in languages like haskell.

If I were to write a double lambda which curried a multiply lambda, for instance, I'd just do this:

multiply = lambda { |a,b| a * b }
double = lambda { |a| multiply[a,2] }

I think this is a lot less scary than a generalized currying lib, and just as powerful.

Maybe I'm missing a killer use case though. The method aliasing is definitely cool:

curry(:double, :*) { [2] }

But what does that get me over:

def double(num)
  num * 2
end

I think the problem with Ruby is it doesn't need stuff like this. :)

Paul added 9 months later:

What I find strange is that the syntax for calling your curried function uses square brackets whereas the syntax for calling regular methods (like "puts", "chop") uses parens. It's an unfortunate aspect of Ruby's design. Python can do most of the same tricks but both the input and the output of the curry are Python "callables" called with parens.

http://www.python.org/dev/peps/pep-0309/

http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52549

Paul added 9 months later:

In Python:

>>> import functools
>>> five_or_higher = functools.partial(max, 5)
>>> five_or_higher(6)
6
>>> five_or_higher(3)
5

According to the Python docs, partial could be implemented like this:

    def partial(func, *args, **keywords):
        def newfunc(*fargs, **fkeywords):
            newkeywords = keywords.copy()
            newkeywords.update(fkeywords)
            return func(*(args + fargs), **newkeywords)
        newfunc.func = func
        newfunc.args = args
        newfunc.keywords = keywords
        return newfunc

Note that this partial function supports both positional and keyword arguments.

Another interesting version looks like this:

>> from partial import _
>>> addWorld = _ + " world!"
>>> addWorld("Hello,")
"Hello, world"

http://www.xoltar.org/2003/jun/25/partialModule.html

Suraj added about 1 year later:

He only used square brackets as a shortcut (i.e. Proc#[] is an alias to Proc#call).

You can replace the square brackets "[...]" in his example with ".call(...)" and things will work just the same.

Add Your Thoughts

You can use Markdown in the body of your comment to format text and make links.

Note that I reserve the right to edit any content you post here. I typically exercise this right to fix formatting issues. All posts must be approved so spam will never been seen on these pages.

Author:
URL or Email (optional):
Body: