Curried Functions

Functions That Take Arguments One at a Time

Sometimes, we gain an advantage from writing functions that are capable of taking their arguments one at a time. I did that in the Session 6 when I created an add-n function:

(define add
  (lambda (n)
    (lambda (m)
      (+ m n))))

We usually think of addition as a binary operation: a function that takes two arguments. add is a function that one argument and returns a function that takes the other.

Why would we ever want to do this? Suppose we are working with blocks of memory and need to index into each block to the same location, or that we need to jump ahead at fixed intervals. In these cases, we know one of the addends at the beginning of the process, but not the other. Or perhaps we are implementing a web site in which clients add items to their carts one at a time, on different web pages. We need the ability to save a partial computation until a later time, at which point we know all of the arguments.

We call functions that take their arguments one at a time curried, in honor of Haskell Curry, a mathematician who explored the idea. A functional programming language, Haskell, was also named in his honor.

The idea is so simple that we have already done it, even before we gave it a name. add curries +:

(define add         ; is a function ...
  (lambda (n)       ; that takes one argument ...                   
    (lambda (m)     ; and returns a function  ...
      (+ m n))))    ; that takes the second arg

Some programming languages, including Haskell, support currying as a primitive feature of every function. In Haskell, for instance:

f x y = x + y      -- a function that adds x to y
(f 1)              -- a function that takes y as an arg and adds 1

Racket does not provide currying automatically, but we can write curried functions ourselves using lambda. To add two numbers, say 6 + 4, we can use add like this:

> ((add 6) 4)      ;; (add 6) returns (lambda (m)
10                 ;;                   (+ 6 m))

This same idea also allows us to name common operations. If we need to add 6 to other values frequently, we can create an add6 function that takes the second argument:

> (define add6 (add 6))
> (add6 4)
10

Why might we want to do this? A curried function allows us to customize a multiple-argument function for use in a specific context. Our add-n function does that. the ring area problem on Homework 3 gives another example. If we are making tables that always need a hole in the middle with a radius of 1 foot, why would we want to pass a 1 as the second argument on every call to ring-area? That's a maintenance error waiting to be made, at potentially great cost.

In our study of programming languages more generally, currying teaches us something important:

A programming language does not have to support functions of more than one argument.

Functions of more than one argument are convenient, but not necessary. Any function of n > 1 arguments can be translated into a function that takes one argument and returns a function of n-1 arguments as its value.

So, we can say that functions of multiple arguments are a syntactic abstraction, a language feature that is not necessary in order to be able to compute any values we might need. We will study other syntactic and data abstractions in greater detail later in the course.

Why is the idea of a syntactic abstraction important to us?

A language interpreter does not need to be able to understand the abstraction.

This is one of the reasons why Racket and Lisp are so easy to extend: they provide macro facilities that allow programmers to create a new syntactic abstraction simply by defining how to translate the new syntax into standard Racket. So, a programmer can add arbitrarily complex expressions to the language without modifying the interpreter or compiler!

We will return to these two big ideas in Unit 3 of the course, Syntactic Abstraction.

Will we find practical uses for curried functions in this course? You bet! We'll use the idea occasionally this semester as we write code to process programs. I hope you recognize the idea when it comes along. I will point it out if you don't.