Ruby is a multi-paradigm language with a strong bias towards object-oriented programming. You can argue that its design is influenced by Alan Kay and Smalltalk, as opposed to C++/Java-style object-oriented languages. Thankfully, this object-oriented design doesn’t mean we can’t use ideas from functional programming. There’s a small list of functional traits in Ruby:

Expression-oriented syntax

Geeky names for Enumerable methods: filter , map , reduce , flat_map

methods: , , , Idiomatic monads

Railway oriented programming

lambdas and procs

… I can go on and on

There’s also one specific empowering feature: built-in support for partial application. In this article, I want to talk about implementation and use-cases for partial application in Ruby.

Proxy methods

Let’s say we have a function of two arguments. As an example, we’ll take a function that accepts two strings: tag and text and formats them to look like [tag] text. The source code for this function would be fairly simple:

def tagged_string ( tag , str ) "[ #{ tag } ] #{ str } " end tagged_string ( 'INFO' , 'Hello, World!' ) # => '[INFO] Hello, World!'

Let’s say we build a logger that only uses 3 tags: INFO, WARNING, and ERROR; and implements only one method, which logs to stdout.

class Log def write ( tag , string ) print ( tagged_string ( tag , string )) end end

So, to use it, we have to always call Log.new.write('INFO', 'log data') , which is not convenient. Besides, what happens if we add different log levels? What if we make a typo, or write DEbUG instead of DEBUG?

Let’s improve the code and write a couple of methods that will help us avoid any mistakes with the tags.

class Log def info ( string ) write ( 'INFO' , string ) end def warning ( string ) write ( 'WARNING' , string ) end def error ( string ) write ( 'ERROR' , string ) end end

Now, instead of a single method with two arguments, we have three methods that accept one argument. Those methods are just proxies to the original method as they don’t have any extra logic; they just fix the first argument of the original method. This technique is called partial application.

Partial application is the process of fixing a number of arguments to a function, producing another function of smaller arity

This Wikipedia definition explains what we’ve done here:

We have a function (write) that accepts two arguments — which means, its arity is 2

We defined functions error, warning, and info that accept only one argument, so their arity is 1

Those functions only pass their input to write write — we fixed the first argument and passed the rest

Whenever we make a function that only calls another one, but requires fewer arguments, we can talk about partial application. In practice, we use it to reduce boilerplate, encapsulate logic, and make our lives easier. I bet you’ve used it countless times already, but probably never considered that this “pattern“ might have a name.

Without methods

What if we don’t want to extend our class, but still want to use partial application? Our only solution is procs. Personally, I prefer to use lambdas for the task. Let’s see how they work.

We have a Log with a simple public interface: write(tag, string). I’m building a system which requires its own tag: SECURITY, but I will only need to use it in one class.

In this case, I would do something like this:

Instantiate the Log and save the object into a variable Define a lambda that uses the object and calls #write on it Use the lambda whenever I want to log something

This is how it looks in a class:

class SecurityService attr_reader :log , :logger_instance def initialize ( logger_instance ) @logger_instance = logger_instance @log = -> ( message ) { logger_instance . write ( 'SECURITY' , message ) } end def call log . call ( 'Hello, World!' ) end end SecurityService . new ( Log . new ). call # => will print "[SECURITY] Hello, World!"

We managed not to define any extra methods, but we still had to manually create a lambda. What if we could avoid it? Then the code would be a little bit simpler:

@log = logger_instance . write ( 'SECURITY' ) log . call ( 'Hello, World!' )

Unfortunately, Ruby doesn’t work this way and we’ll just get an exception. However, it gives us a couple of tools to implement what we want.

Metaprogramming

Let’s use Ruby’s metaprogramming to write a helper will enable us to pass fewer arguments to our methods. Here’s how it would work:

You pass a function to the helper

Helper returns a modified function

If we call the modified function and provide all arguments, it works as usual

If we provide fewer arguments than required, we get a new function that requires the rest of the arguments

This is how it would look like:

enable_partial_application = ... # our helper fun = -> ( x , y ) { x + y } # our function new_fun = enable_partial_application . call ( fun ) plus_two = new_fun . call ( 2 ) # => new function plus_two . call ( 3 ) # => 5 plus_two . call ( 10 ) # => 12 new_fun . call ( 4 , 3 ) # => 7

Sounds cool, right? Let’s see how we can implement this in Ruby. I’m going to pollute global namespace and define a method enable_partial_application that accepts a function and returns a wrapper function.

def enable_partial_application ( fun ) -> ( * args ) { fun . call ( args ) } end

Let’s start adding logic piece-by-piece. Here’s first piece of logic: “If we provide enough arguments, we call the original function”. To do so, we need to know exactly how many arguments the function requires — so we use the built-in method #arity, which gives us the number.

def enable_partial_application ( fun ) arity = fun . arity -> ( * passed_args ) { # I use `<=` instead of `==` because I want Ruby to # handle cases when there are too many arguments. if arity <= passed_args . count fun . call ( * passed_args ) else # ??? end } end

Alright, let’s handle the case when there are fewer arguments than required. We’ll return a new function that remembers our previous input:

def enable_partial_application ( fun ) arity = fun . arity -> ( * passed_args ) { if arity <= passed_args . count fun . call ( * passed_args ) else -> ( * args ) { fun . call ( * passed_args , * args ) } end } end

Okay, now we can test it:

fun = -> ( x , y ) { x + y } new_fun = enable_partial_application ( fun ) plus_two = new_fun . call ( 2 ) plus_two . call ( 3 ) # => 5 plus_two . call ( 10 ) # => 12 new_fun . call ( 4 , 3 ) # => 7

It works, alright. Let’s check out functions with more arguments:

fun = -> ( x , y , z ) { x + y + z } new_fun = enable_partial_application ( fun ) new_fun . call ( 2 , 3 ). call ( 3 ) # => 8 plus_two = new_fun . call ( 2 ) plus_two . call ( 3 , 1 ) # => 6 plus_two . call ( 3 ). call ( 1 ) # => ArgumentError # (wrong number of arguments (given 2, expected 3))

The last line fails because we need to make enable_partial_application work recursively. We can fix this by updating two lines:

def enable_partial_application ( fun ) arity = fun . arity apply = -> ( * passed_args ) { # <= if arity <= passed_args . count fun . call ( * passed_args ) else -> ( * args ) { apply . call ( * passed_args , * args ) # <= } end } end

Now it works like a charm — the resulting function keeps calling itself until the user has provided enough arguments.

One last thing though. It doesn’t work with functions that take a dynamic number of arguments because fun.arity returns a negative value. It’s a weird [built-in behavior(https://ruby-doc.org/core-2.2.0/Proc.html#method-i-arity) of procs. There’s a simple fix:

arity = fun . arity # replace with arity = fun . arity . positive ? fun . arity : - fun . arity - 1

Finally, we’ve got a working helper that enables partial application for any function in Ruby. The final result:

def enable_partial_application ( fun ) arity = fun . arity . positive ? fun . arity : - fun . arity - 1 apply = -> ( * passed_args ) { # <= if arity <= passed_args . count fun . call ( * passed_args ) else -> ( * args ) { apply . call ( * passed_args , * args ) # <= } end } end

Built-in method

Thankfully, we don’t need to build those helpers. Ruby has a built-in method called #curry , which works on methods and procs, and does everything I’ve described above.

fun = -> ( x , y , z ) { x + y + z } new_fun = fun . curry new_fun . call ( 2 , 3 ). call ( 3 ) # => 8 plus_two = new_fun . call ( 2 ) plus_two . call ( 3 , 1 ) # => 6 plus_two . call ( 3 ). call ( 1 ) # => 6

This method takes its name from currying, a process of transforming a single function of N arguments into N functions that only take a single argument. It’s a technique to assist partial application in statically typed functional languages like Haskell, OCaml, and F#. It’s a topic for a separate article so I won’t mention the details.

Recap

Partial application helps us fix values and pass fewer arguments

We use it quite often, even if we don’t do it explicitly

If a function has a variable number of arguments, its arity is negative

We need a recursive function to build our own partial application

Ruby comes with partial application out of the box: Proc#curry and Method#curry

and Currying is less performant than plain methods/procs

Note: if you want to try currying, please keep in mind that it’s not a popular pattern in Ruby, so your colleagues might be skeptical about it. However, I urge you to try it out and compare with the conventional partial application.

References