So they are different concepts, but both are related to nested lambdas.
A Closure can be created by a lambda that refers to a variable defined outside itself, and is most important when the lambda escapes from the context where that outside variable is defined. The Closure's job is to make sure that variable is preserved when the lambda escapes that context.
A Curried function is a function that can take its arguments in multiple steps, or multiple different function-applications. This normally means there are lambdas nested within lambdas.
Curried functions aren't always closures, though they often are
Most useful curried functions need to use closures, but if the inner lambdas ignore the outer arguments, they aren't closures. A simple example:
(define (curried-ignore-first ignored)
(lambda (y) y))
This is not a closure because the inner lambda (lambda (y) y)
is already closed: it doesn't refer to any variables outside itself.
A curried function doesn't always need to ignore the outer arguments... it just needs to be done processing them before it returns the inner lambda, so that the inner lambda doesn't refer to the outer argument. A simple example of this is a curried choose
function. The "normal" definition of choose
does indeed use a closure:
(define (choose b)
(lambda (x y)
(if b x y))) ; inner lambda refers to `b`, so it needs a closure
However, if the if b
is put outside the outer lambda, we can avoid making closures:
(define (choose b)
(if b
(lambda (x y) x) ; not closures, just nested lambdas
(lambda (x y) y)))
Closures aren't always from curried functions
A closure is needed when a inner lambda refers to a variable in an outer context and might escape that context. That outer context is often a function or a lambda, but it doesn't have to be. It can be a let:
(define closure-with-let
(let ([outer "outer"])
(lambda (ignored) outer))) ; closure because it refers to `outer`
This is a closure, but not an example of currying.
Turning a Curried-function-producing-a-closure into one without a closure
The example in the original question is a curried function that produces a closure
(define (make-an-adder x)
(lambda (y)
(+ y x)))
If you wanted to make a version that's still a curried function with the same behavior, but without needing a closure over x
in some special cases, you can branch on those before the lambda:
(define (make-an-adder x)
(match x
[0 identity]
[1 add1]
[-1 sub1]
[2 (lambda (y) (+ y 2))]
[3 (lambda (y) (+ y 3))]
[_ (lambda (y) (+ y x))]))
This avoids producing a closure for the cases of x
being an exact integer -1 through 3, but still produces a closure in all other cases of x
. If you restricted the domain of x
to a finite set, you could turn it into a function that didn't need closures, just by enumerating all the cases.
If you don't want a closure over x
, but you're fine with a closure over other things, you can use recursion and composition to construct an output function that doesn't close over x
:
(define (make-an-adder x)
(cond [(zero? x) identity]
[(positive-integer? x)
(compose add1 (make-an-adder (sub1 x)))]
[(negative-integer? x)
(compose sub1 (make-an-adder (add1 x)))]))
Note that this still produces closures (since compose
creates closures over its arguments), but the function it produces does not close over x
. Once this version of make-an-adder
produces its result, it's "done" processing x
and doesn't need to close over it anymore.