Why is curry not popular among Rubyists?

I’ve been wondering this one for a while. In fact, as much as I like functional programming, most of the time my Ruby functions are not curried or partially applied in any way. I guess this is because I have always thought of Ruby as a very paradigmatic Object Oriented language, where absolutely everything is an object, Smalltalk is at its core and even I do find myself writing code in lambdas and blocks all the time like most Ruby programmers do, I’ve stayed away from curry and partial application in production code.

If I was asked why do I do this, I would say it’s just a matter of following the best Rubyists style, and keeping my code easy to understand for newbies. The former argument is weak at best –fallacious argument from authority– while the second one is true, but I consistently use other advanced features that newbies won’t understand at a first glance. Moreover, lazy evaluation is about to come in Ruby 2.0 and I am very, very positive its use will become very widespread.

If you care about this and treat it as a problem, it’s probably just a matter of being afraid of criticism for not adhering to the rules. It’s so easy to follow that route and shun from challenging the state of things. I’ll make a conscious effort from today on using it unless someone raises a valid point against using these two functional approaches in Ruby code. And to start off I am going to give a brief overview of it in this post.

In any case, Ruby core thought it was a good idea and allows us to make curried Procs from 1.9 on. So here is what I understand as currying and partial application in Ruby, how to do it, you can draw your own conclusions.

Currying is a concept that allows a function that takes N parameters to be a composition of N functions, each of them take 1 parameter.

If you are looking at this and your eyebrow is raising,  bear with me for one minute.

Functions have arguments. When a function is called using all its arguments it means we are applying all of the arguments to the function we are calling to. In the non-curried function above, we are applying ‘x, y, z’ to the function.

Curried functions allows us to define new functions in terms of partially applied functions. A few examples will clarify how this is relevant and very useful feature in a programming language.

The original way (ML family of languages):

Many functional languages will let you write  “f x y z".  If you call “f x y" then you get a partially-applied function—the return value is a closure of lambda(z){z(x(y))} with passed-in the values of x and y to f(x,y).

The Ruby way:

Partial application is not as natural to write in Ruby as it is in Haskell or SML. Functions are not curried by default and in fact the way to do this is to curry the function ourselves, then define new partially applied functions upon the curried function. This is a simple function that only adds up numbers from a to b, applying f to them.

The power that partial application is giving us here is that we can very easily define new functions that build up on sum by using partial application. 

For instance,  we can partially apply the function f, and get a function ‘sum_of’_squares’ that only requires the start and end of the interval.

Or we can even partially apply the function f and the start of the interval a, and provide a more specific function:

Of course we can pass functions that remove prime numbers from the sum, start the interval or end it wherever we want[1]. These are all useful things when building a set of abstractions for your domain. Will you start to use them?

 

[1]: Actually no, we have only explored leftmost currying in this article. Rightmost currying is not currently implemented in Ruby, I’ll do my best to have it ready for 2.0.0

[2]: Last but not least, here’s an example of an interesting use of partial application in a context, by PragDave aka The Pragmatic Programmer.

  • I’d say the reason is pretty simple: for currying to be idiomatic, your language’s libraries must explicitly support it by ordering arguments in the way convenient for partial application. It also helps if the language supports currying of keyword arguments (like OCaml).

    Blocks are also a problem: they don’t participate in currying, because every Ruby method can potentially take a block.

  • Kenn Ejima

    Off topic but the permalink of this post is borked.

  • As someone who has been writing Ruby for 3 years, but hasn’t worked seriously with a functional programming language, I’ve been excited to read and learn about currying, but I haven’t been able to see a lot of situations where it would make sense to use.

    Pat Shaughnessy gave an excellent talk detailing some of the functional programming inspired features in Ruby prior to 2.0 and some additional features coming in Ruby 2.0. He compares examples in Ruby with examples in Haskell and at the end details a bunch of areas in which Ruby falls short of a fully featured functional programming language. Here is a link to the video of the talk:

    http://www.youtube.com/watch?v=8DvxOLzmLSI

    This talk got me a lot more interested in exploring additional hidden functionality that I wasn’t using in Ruby objects, but where I’ve really struggled is identifying problems that can be elegantly solved with currying or lazy loading. I suspect that I, and possibly other Ruby programmers with primarily object oriented programming backgrounds, are lacking the paradigm that other developers who have spent a lot of time learning a purely(or at least primarily) functional language have to see how to use this kind of functionality and how it all fits together. Additionally, very few code bases that most ruby developers are working with will have examples of well executed currying functions, very few books or blog posts that describe patterns for solving particular kinds of problems or organizing code will use currying and the end result is that there just aren’t a lot of Ruby examples for developers who think this stuff is really cool, but aren’t sure how to apply it to what they’re working on.

    I do really like the example that you provided from PragDave. I hope that I eventually reach a comfort level with currying where its immediately apparent at a glance what is happening.