6 ms·
Objects are a poor man's closures (2003)
- JoelMcCracken 17y agoApples are a poor man's oranges.
- SlyShy 17y agoI don't think this analogy applies, insofar as you can't implement apples with oranges. ;) However, anyone who has worked through SICP will tell you, you can implement objects with closures.
- _delirium 17y agoTrue, though as the article points out, you can also implement closures with objects. Of course, you can also implement both in C. ;-)
- stcredzero 17y agoIn certain languages you can accomplish closures and objects without enough rope to hang yourself in truly spectacular fashion. Seems a reasonable personal preference when put that way.
- stcredzero 17y agoEncapsulation and the rule of thumb to avoid temporary variables in methods is OO's cut-rate version of functional programming. It has the advantage of being more easily accessible to minds accustomed to structured procedural programming. What's more, is that it works "well enough" to at least be seductive. In Smalltalk, I can edit a method in the debugger and throw away the top part of the stack and restart in the current context yet only rarely deal with side effects. Granted, this is probably not good enough for something like a VOIP sever process relying on shared nothing assumptions for a robust concurrent architecture, but this may be one of the reasons that many OO programmers aren't eager to make the leap. (Similar to explaining the utility of unhampered closures to some Python programmers: you can do a whole lot even with Python's limited blocks. Why is more needed?)
- jerf 17y agoThat's not an accurate representation of the Pythonic argument. The argument is more along the lines that having to name your blocks is not a crippling requirement, and that you can do everything you want with Python functions in 3.0 with the nonlocal keyword, and nearly as much pre-3.0 as you rarely need deeply nested scopes in Python because of the other things it supports. The thing that frustrates Pythonistas is people mistaking syntax for capability. Far be it from me to claim that syntax doesn't matter, but far too many claim that things aren't possible in Python when in fact you're just spelling it wrong. (And there are some pros to the Python approach; naming even modestly large functions is still generally a good idea, and Python's approach to the problem is uniform vs. Ruby's "several different type of code references" approach, which IMHO has the Perl problem of being gloriously more complicated and tricking people into thinking these are great advantages from a local perspective when in fact if you back out to the global perspective it turns out Python still does the same things, only without the complication.)
- jimbokun 17y agoDoes the nonlocal keyword fix the problems resulting from Python lacking lexical scope? The other issue, if I recall correctly, is that multi-line anonymous function syntax doesn't work because of Python's significant whitespace. "...as you rarely need deeply nested scopes in Python because of the other things it supports." This encapsulates pretty well the relative philosophical differences between Scheme and Python, I think. Scheme focuses on giving you fairly simple yet powerful syntax and semantics as building blocks from which you can build the sophisticated structures and control flow you need. Python focuses on building sophisticated structures and control flow directly into the language, so you need not be as concerned about the building blocks of the language. [Example: list comprehensions] Is that fair?
- jerf 17y ago"Does the nonlocal keyword fix the problems resulting from Python lacking lexical scope?" Most of them. Needing it in Python is still an enormous design smell, though. It is very unlikely that it is the correct solution to your problem. "The other issue, if I recall correctly, is that multi-line anonymous function syntax doesn't work because of Python's significant whitespace." The Pythonic objection would be that you are getting awfully tied up in the word "anonymous". Even though I no longer think of myself as a "Pythonista", and even though I'm getting ever closer to Haskell, I still just do not get people's obsession with the "anonymous" bit. If it's small, use lambda. If it doesn't fit in a lambda, give it a name; compared to the body of the function how large is it, anyhow? Anonymity is not the important thing, the closure is. One might as well complain that I am forced to name the parameters of functions. After all, I don't need to in Perl, why should I have to in your language? But exactly the same rules apply; using $_[0] in a one-liner is one thing, but even as soon as a five-line function, give it a name. "Python focuses on building sophisticated structures and control flow directly into the language, so you need not be as concerned about the building blocks of the language." Close, but this does miss one critical aspect of Python, which is that most of the syntax can be easily and effectively overloaded. Dot and square bracket can be overloaded, "properties" are actually special-cases of a more general facility ("descriptors" if you want to Google it), and so on (for quite a while). It is deliberately a balance between a highly fungible language like Scheme and a closed language like C. One of the things I learned from Python is that the number of things in your language that can't be usefully overridden by the user is an important criterion by which you can judge your later frustration level. Does your language hard code the module import procedure? Then how would you implement the Python "zipfile" module in your language? Does your language hard code what binding means? Then how would you implement "property" in your language? As a special case? Then how am I supposed to override that when I want something else? Does your class system have no concept of metaclass? Then you can't add it on later with any degree of effectiveness. Note by no means am I claiming Python is the only language with this characteristic, it's just where I learned this principle. It's really hard for a general-purpose language to be so incredibly right in every way that it can hard code any of these things without causing trouble later. Typically communities that don't have these features grow enormous blind spots around them over time. (Incidentally, even very "dynamic" languages can still end up with hard-codings in places that you might not even think of until you see another language that does it differently. Python/Perl-style overriding of the package load module is one example. In the world of C it simply never even occurred to me that maybe #include was way too hard-coded. How different would our technical history be if C had been able to actually do useful logic at compile time with includes, instead of it all ending up in Makefiles and config scripts? I'm not saying it would be better, just that it would have been different.) PS: why is deeply-nested scope in Python a design smell? Rather than creating a closure of such complexity, you probably should be creating a class that implements __call__. This has all the features a class has available to it, it's very easy for the "call" to be nothing but a dispatch out to a generator if that's what you want, while at the same time being everything a closure can be. Needing three or four layer deep closures is the Pythonic equivalent to nesting your loops three or four layers deep. It may be the best solution in other languages, but it's not in Python. I think not very many people realize that creating such a class is one method declaration away. If you're doing significant programming in Python and you never override any of the double-underscore methods you're probably doing it wrong. (I tend to stay away from the equality ones, but the rest are fair game. Do you have any __iter__ methods? You probably should.)
- j_baker 17y agoBut are oranges a poor man's apples?
- billybob 17y agoTrue. In addition: -Dogs are a poor man's velociraptors -Toothpaste is a poor man's hair gel -Rocks are a poor man's sandwich ingredients
- btilly 17y agoThe title should be amended to point out that this is from 2003. My summary of the difference has always been, "Objects make good nouns while closures make good verbs."
- barrkel 17y agoUnfortunately, in many situations you also need to ask noun-like questions about your closures. For things like loop bodies, conditionals passed to filters, comparisons passed to sorts, etc., closures work very well. But when you start using a closure to e.g. represent an I/O endpoint (e.g. () => string for reading, string => () for writing to / from a console), you'll also want to do things like flush buffers.
- btilly 17y agoThat's why closures are a poor man's objects. :-)
- loup-vaillant 17y agoWhen, not why :-)
- AndrewDucker 17y agoI do like that the poster is open minded and educated enough to realise that both are reflections of each other. I particularly like the koan at the end.
- wingo 17y agoThe poster is Guy Steele, who when implementing Scheme, found out that his implementation of objects (actors) was actually the same as his implementation of closures. [Edit: the poster is actually Anton van Straaten, replying to Steele]
- zitterbewegung 17y agoAlso anton van straaten edited r6rs.
- d0m 17y agoWith macro and closures, you can implement your objects as you wish; so in this way of thinking, you don't need to "build it again and again"... it's just that depending of the problem, there are better ways. (i.e. sort_by vs overriding operator< or building a functor someone?)
- eplanit 17y agoHere's one very content poor man, then.
- j_baker 17y agoSounds a bit like Maslow's hammer, does it not? http://en.wikipedia.org/wiki/Law_of_the_instrument http://en.wikipedia.org/wiki/Law_of_the_instrument
- gcb 17y agoas a computer scientist, he's a good car salesman.
- asimjalis 17y agoOne difference between objects and closures is that objects require explicitly passing state into them, while closures can capture state from their context. This is powerful because it lets you explore different abstractions cheaply with closures. Programming in Lisp I frequently stumble upon abstractions that I would not have noticed if I was programming in Java or some other closure-less language. For example, a button is just a closure. It supports exactly one operation: click.
- j_baker 17y agoThis can also be a bad thing. If you explicitly pass in parameters to something, it's much easier to tell where certain pieces of data are coming from.
- asimjalis 17y agoRight. More explicit means less fluid. So there is a trade-off. Fluidity might not be important when the design is well-known and fixed. But if you want to use code as a medium for exploring design, then fluidity is powerful.
- basman 17y agoOne problem I've noticed with a very closure-heavy style is that debugging is a pain — you have a closure with a bunch of local state and no way to print or otherwise access it (from the repl, say). So then you end up writing special cases of the function for when the first argument is :debug... any better ways to do this?
- oconnore 17y agoYou could use a built in object system instead of trying to roll your own? Closures are wonderful, but when you have access to a high quality piece of software designed to make holding state easy, why not use it?
- tumult 17y agoDon't mutate data structures from within a closure :)
- blueag 17y agoWhy shouldn't I mutate local data structure?
- tedunangst 17y agoEven if you're not mutating the state, you may be interested in knowing what it is.
- loup-vaillant 17y agoJust look at the definition of your closure. If you didn't mutate the state since then, well, it hasn't changed. Some debuggers even allow you to "travel back in time", so you can easily "debug" the definition of that closure.
- wingo 17y agoFix your language implementation: a debugger should have access to a closure's captured state.
- nopassrecover 17y agoPencils are a poor man's keyboard. There are different sets of things you can do with both. You might be able to draw on a screen with the arrow keys but that doesn't mean you should.
- deleted 17y ago[deleted]