77

Closures are poor man's objects and vice versa.

I have seen this statement at many places on the web (including SO) but I don't quite understand what it means. Could someone please explain what it exactly means?

If possible, please include examples in your answer.

Community
  • 1
  • 1
missingfaktor
  • 90,905
  • 62
  • 285
  • 365

7 Answers7

79

Objects are poor man's closures.

Consider Java. Java is an object-oriented programming language with no language level support for real lexical closures. As a work-around Java programmers use anonymous inner classes that can close over the variables available in lexical scope (provided they're final). In this sense, objects are poor man's closures.

Closures are poor man's objects.

Consider Haskell. Haskell is a functional language with no language level support for real objects. However they can be modeled using closures, as described in this excellent paper by Oleg Kiselyov and Ralf Lammel. In this sense, closures are poor man's objects.


If you come from an OO background, you'll probably find thinking in terms of objects more natural, and may therefore think of them as a more fundamental concept than closures. If you come from a FP background, you might find thinking in terms of closures more natural, and may therefore think of them as a more fundamental concept than objects.

Moral of the story is that closures and objects are ideas that are expressible in terms of each other, and none is more fundamental than the other. That's all there is to the statement under consideration.

In philosophy, this is referred to as model dependent realism.

Miikka
  • 4,573
  • 34
  • 47
missingfaktor
  • 90,905
  • 62
  • 285
  • 365
  • 8
    There's a small difference in that, in objects, state is explicitly defined on object instantiation. In closures, state comes from an unknown outer scope. Thus, using objects results in more decoupled, explicit, and testable code. – dtheodor Feb 29 '16 at 11:13
  • 5
    Some languages require capture of variables in a closure meaning the scope is explicitly defined. – Nick Long Apr 28 '17 at 17:52
  • 5
    @dtheodor I disagree with your statement: The state of a closure is always predictable by reading from the top up to the closure. The state of an object (at least the object-oriented style), however, happens to be whatever it currently happens to be, since state and code are intermingled. Closures are [referential transparent](https://en.wikipedia.org/wiki/Referential_transparency) while objects are not. – Qqwy Oct 20 '17 at 12:45
  • 2
    I've been using Python a lot, and I find closures to be more readable in most cases since objects require lots and lots of code for the constructor, methods, and data initializing. Objects tend to change their internal state leading to "goto" like errors for programmers that aren't used to them, while closures tend to be used for objects that don't change state much. Closures seem like "for loops" while objects seem like "while loops" to me as in for loops require less code, more common, and less scope for errors. While loops are sometimes needed, however. – aoeu256 Nov 01 '17 at 16:39
  • 4
    @aoeu256 Mutability is an orthogonal dimension. You can close over mutable variables both in plain closures and objects alike. You can similarly choose to have only immutable variables in objects, as well as in plain closures. – missingfaktor Nov 02 '17 at 17:15
62

The point is that closures and objects accomplish the same goal: encapsulation of data and/or functionality in a single, logical unit.

For example, you might make a Python class that represents a dog like this:

class Dog(object):
    def __init__(self):
        self.breed = "Beagle"
        self.height = 12
        self.weight = 15
        self.age = 1
    def feed(self, amount):
        self.weight += amount / 5.0
    def grow(self):
        self.weight += 2
        self.height += .25
    def bark(self):
        print "Bark!"

And then I instantiate the class as an object

>>> Shaggy = Dog()

The Shaggy object has data and functionality built in. When I call Shaggy.feed(5), he gains a pound. That pound is stored in variable that's stored as an attribute of the object, which more or less means that it's in the objects internal scope.

If I was coding some Javascript, I'd do something similar:

var Shaggy = function() {
    var breed = "Beagle";
    var height = 12;
    var weight = 15;
    var age = 1;
    return {
        feed : function(){
            weight += amount / 5.0;
        },
        grow : function(){
            weight += 2;
            height += .25;
        },
        bark : function(){
            window.alert("Bark!");
        },
        stats : function(){
            window.alert(breed "," height "," weight "," age);
        }
    }
}();

Here, instead of creating a scope within an object, I've created a scope within a function and then called that function. The function returns a JavaScript object composed of some functions. Because those functions access data that was allocated in the local scope, the memory isn't reclaimed, allowing you to continue to use them through the interface provided by the closure.

miikkas
  • 818
  • 1
  • 8
  • 25
BenTrofatter
  • 2,148
  • 1
  • 13
  • 13
15

An object, at its simplest, is just a collection of state and functions that operate on that state. A closure is also a collection of state and a function that operates on that state.

Let's say I call a function that takes a callback. In this callback, I need to operate on some state known before the function call. I can create an object that embodies this state ("fields") and contains a member function ("method") that performs as the callback. Or, I could take the quick and easy ("poor man's") route and create a closure.

As an object:

class CallbackState{
    object state;

    public CallbackState(object state){this.state = state;}

    public void Callback(){
        // do something with state
    }
}

void Foo(){
    object state = GenerateState();
    CallbackState callback = new CallbackState(state);
    PerformOperation(callback.Callback);
}

This is pseudo-C#, but is similar in concept to other OO languages. As you can see, there's a fair amount of boilerplate involved with the callback class to manage the state. This would be much simpler using a closure:

void Foo(){
    object state = GenerateState();
    PerformOperation(()=>{/*do something with state*/});
}

This is a lambda (again, in C# syntax, but the concept is similar in other languages that support closures) that gives us all the capabilities of the class, without having to write, use, and maintain a separate class.

You'll also hear the corollary: "objects are a poor man's closure". If I can't or won't take advantage of closures, then I am forced to do their work using objects, as in my first example. Although objects provide more functionality, closures are often a better choice where a closure will work, for the reasons already stated.

Hence, a poor man without objects can often get the job done with closures, and a poor man without closures can get the job done using objects. A rich man has both and uses the right one for each job.

P Daddy
  • 28,912
  • 9
  • 68
  • 92
7

EDITED: The title of the question does not include "vice versa" so I'll try not to assume the asker's intent.

The two common camps are functional vs imperative languages. Both are tools that can accomplish similar tasks in different ways with different sets of concerns.

Closures are poor man's objects.

Objects are poor man's closures.

Individually, each statement usually means the author has a some bias, one way or another, usually rooted in their comfort with one language or class of language vs discomfort with another. If not bias, they may be constrained with one environment or the other. The authors I read that say this sort of thing are usually the zealot, purist or language religious types. I avoid the language religious types if possible.

Closures are poor man's objects. Objects are poor man's closures.

The author of that is a "pragmatist" and also pretty clever. It means the author appreciates both points of view and appreciates they are conceptually one and the same. This is my sort of fellow.

codenheim
  • 20,467
  • 1
  • 59
  • 80
  • 1
    I think you missed the "and vice versa" part of the statement. – Chris Conway Mar 23 '10 at 12:33
  • No, I didn't miss it, I just misread it. I just wasn't sure if the asker meant it was actually part of the quote, or if he meant he saw both versions of a quote. Now that you point it out, I see what you mean, the author of the statement is likely UN-biased, appreciating both sides, but probably mediating a discussion between some "zealous, purist, language religious types". – codenheim Mar 23 '10 at 15:38
  • The title of the question is "Closures are a poor man's objects" without the "vice versa", so I'm not sure which one the asker is asking. I obviously answered the title, and possibly not the body. Oh well. – codenheim Mar 23 '10 at 15:55
5

"Objects are a poor man's closures" isn't just a statement of some theoretical equivalence — it's a common Java idiom. It's very common to use anonymous classes to wrap up a function that captures the current state. Here's how it's used:

public void foo() {
    final String message = "Hey ma, I'm closed over!";
    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            System.out.println(message);
        }
    });
}

This even looks a lot like the equivalent code using a closure in another language. For example, using Objective-C blocks (since Objective-C is reasonably similar to Java):

void foo() {
    NSString *message = @"Hey ma, I'm closed over!";
    [[NSOperationQueue currentQueue] addOperationWithBlock:^{
        printf("%s\n", [message UTF8String]);
    }];
}

The only real difference is that the functionality is wrapped in the new Runnable() anonymous class instance in the Java version.

General Grievance
  • 4,555
  • 31
  • 31
  • 45
Chuck
  • 234,037
  • 30
  • 302
  • 389
  • This example demonstrates that Java doesn't really have full closure support. That's way we need the message to be final. – Wei Qiu Apr 12 '14 at 20:39
5

Just so much sugar, as closures hide anonymous objects under their skirts.

DaGreek
  • 67
  • 1
  • 1
  • 2
    +1 - At the VM / compiler level, everything is really an "object" of some sort. It's all in a name. Those of us who've implemented compilers know the concerns are similar. Anything that must be garbage collected is an object deep down. – codenheim Jan 05 '11 at 17:53
  • 3
    This is a dummy answer, actually. It doesn't matter if they're the same thing for the compiler or interpreter. What it matters is their value as abstractions. Otherwise, I could say that everything is just 0 and 1, which isn't really useful. – YuriAlbuquerque Jun 20 '13 at 00:45
  • 1
    @YuriAlbuquerque If we go down to the turing machine model, there are no 0's; everything is a 1. – Alice Jun 13 '14 at 11:49
1

That objects can be used as a replacement for closures is quite easy to understand, you just place the captured state in the object and the calling as a method. Indeed for example C++ lambda closures are implemented as objects (things are sort of tricky for C++ because the language doesn't provide garbage collection and true closures with mutable shared state are therefore hard to implement correctly because of the lifetime of captured context).

The opposite (closures can be used as objects) is less observed but it's IMO a very powerful technique... consider for example (Python)

def P2d(x, y):
    def f(cmd, *args):
        nonlocal x, y
        if cmd == "x": return x
        if cmd == "y": return y
        if cmd == "set-x": x = args[0]
        if cmd == "set-y": y = args[0]
    return f

The function P2d returns a closure that captured the two values of x and y. The closure then provide access for reading and writing to them using a command. For example

p = P2d(10, 20)
p("x") # --> 10
p("set-x", 99)
p("x") # --> 99

so the closure is behaving like an object; moreover as any access is going through the command interface it's very easy to implement delegation, inheritance, computed attributes etc.

The nice book "Let Over Lambda" builds over this idea using Lisp as a language, but any language that supports closures can use this technique (in Lisp the advantage is that you can also bend the syntax using macros and read macros to improve usability and automatically generate all boilerplate code). The title of the book is exactly about this... a let wrapping a lambda:

(defun p2d (x y)
   (let ((x x) (y y))
     (lambda (cmd &rest args)
       (cond
          ((eq cmd 'x) x)
          ((eq cmd 'y) y)
          ((eq cmd 'set-x) (setq x (first args)))
          ((eq cmd 'set-y) (setq y (first args)))))))

Actually I'm not sure I agree with the "poor" adjective in this approach.

6502
  • 112,025
  • 15
  • 165
  • 265