13 ms·
“A closure is a poor man’s object; an object is a poor man’s closure” (2003)
- sevensor 9y ago(2003) My favorite thing about this thread is that Guy Steele is involved, and Guy Steele has a far better name than anybody else has ever had.
- apathy 9y agoI did work with a guy named Max Kalashnikov at Google. That was a pretty "rad" name, too.
- sevensor 9y agoI've also known a "Ulises Cervantes" and an "Aristotle Socrates," since we're trading awesome names :) Guy Steele is still my favorite, because I can just picture a mustachioed villain shouting "curse you Guy Steele!"
- apathy 9y agoThe visual here is awesome. Although all good programmers are antiheroes in my world, so I read that as a mustache twirling Guy Steele merrily evading the bumbling cops. Practically the whole point of Real Programming is to evade the limits of The Man. (Cf Mel: http://www.catb.org/jargon/html/story-of-mel.html http://www.catb.org/jargon/html/story-of-mel.html )
- dherman 9y agoGuy Steele's friend Olin Shivers [1] was the PhD advisor of Matt Might [2] and Lex Spoon [3]. I'm pretty confident the entire field of programming languages research is a Marvel comic. [1] http://www.ccs.neu.edu/home/shivers/ http://www.ccs.neu.edu/home/shivers/ [2] http://matt.might.net/ http://matt.might.net/ [3] https://www.lexspoon.org/ https://www.lexspoon.org/
- jfoutz 9y agoI enjoyed building object systems in scheme. The closure way is kind of like focusing on the code without worrying to much about the underlying data structure. The object way is worrying about the data structure and the code is secondary.
- Tloewald 9y agoI think in effect you're saying that they're duals. I think I would argue that objects bundle data and functions in a useful way (when done right). Closures are more of an expressive convenience for constructing functions on-the-fly. The overreach for objects is to consider them as the only way to organize data and functions.
- jfoutz 9y agoDuals has the right flavor, but my opinion is just an opinion, i won't try to defend that with any kind of formalism duals would imply. I agree with you wrt objects bundle data and functions, but for my code, that sometimes causes cracks, disagreements about how i mentally model the code vs how i have to write it out. For example the visitor pattern is this little syntactic boilerplate dance to get a specific kind of behavior collected in one file. The closure way, you can build a special dispatch, and put all the implementations there. I don't think there's a right answer. I just go along with what i think people will understand. There's a lot of value in having a standard and well understood dispatch system we all understand, even if it gets sort of odd in some cases. With the closures, dispatching can be as crazy as you want. Create cycles of A extends B, B extends C, and C extends A for example.
- ionforce 9y agoWhat is a dual?
- recursive 9y agoIt's a table in all Oracle databases that has nothing in it. Also, it's this: https://en.wikipedia.org/wiki/Duality_(mathematics) https://en.wikipedia.org/wiki/Duality_(mathematics)
- adamnemecek 9y agoScala programmers are laughing from the side lines.
- throwa38282 9y agoWhich is where they'll remain. (snark begets snark)
- noblethrasher 9y agoF# also has object expressions that are just as syntactically convenient as closures.
- saghm 9y agoFrom what little I know of F#, it seems a lot like OCaml (as in, I don't know enough to know of any differences, other than that F# is a .NET language). From what slightly more I know about OCaml, objects are there, but almost nobody uses them and everyone pretends that they're not in the language. Is F# similar in this regard, or are objects considered fair game?
- noblethrasher 9y agoIdiomatic F# code does tend to use pattern matching more than ad-hoc polymorphism, but objects are still very much considered fair game since the major value proposition of the language is its ability to leverage the .NET ecosystem.
- kbd 9y agoAs Scala didn't yet exist when this message was written, its author was missing the enlightenment Scala provides. Would you please explain what about this amuses Scala programmers?
- throwa38282 9y agoScala is largely a set of iterative improvements with a syntax that's designed to handle data workflows well. It's not a big deal, it's not that exciting, it doesn't use all of the latest technologies, and that's why I think it has a bright future.
- Animats 9y agoThe main use of closures today is to create something you can pass to a callback. In theory, you could pass an object, but the callback would have to cooperate by accepting an object and an object method, or a reference to an object method/object pair, in languages which support that. This requires more standardization than one is likely to get.
- saghm 9y agoI think of closures as a bit orthogonal to that; the definition of "closure" that I'm used to is a function that "encloses" some data that is not an argument (somewhat like a function with free variables in lambda calculus), whereas a callback might only deal with its arguments. That being said, sometimes I see the term "closure" being used in a way that I associate with the word "lambda", which feels a bit off to me (I'm looking at you, Rust...)
- cperciva 9y agoCallbacks only deal with their arguments, but almost every callback I see in the wild has a first argument "void * cookie" which immediately gets typecast into a pointer to the structure (oops, I mean object) being manipulated.
- Animats 9y agoRight, that's how you do it in C, where you don't have closures. Closures take some heavy machinery behind the scenes in a hard-compiled language. They sort of fall out of the dynamic binding mechanism in Python and Javascript.
- Animats 9y agoA lambda is an anonymous function. A closure is a function which carries with it some state. The two concepts are independent - you can have named functions as closures, and lambdas that aren't closures. Rust does seem to tie the concepts together syntactically. Languages which support nested functions usually allow those inner functions to be closures.
- derefr 9y agoMy definition of an object is "an ADT that gets to 'decide', using private runtime state, what to do in response to any/all attempts to interact with it." Which makes for a very simple test: if something is an object, you should be able to send it a message that makes it change its mind about what messages it accepts after that. There's no obvious way to implement a behavior like this using the native "objects" in nominally-object-oriented languages like C++ or Java. (But you can do so with the closure types from these languages. Or you can look higher on the stack: threads with IPC queues, or POSIX processes, are both objects.) But implementing this behavior is obvious in Ruby; this is how basic things like Object#extend work. And implementing this behavior is also obvious, oddly enough, in Erlang: http://joearms.github.io/2013/11/21/My-favorite-erlang-program.html http://joearms.github.io/2013/11/21/My-favorite-erlang-progr... In conclusion, Erlang is more object-oriented than Java. ;) --- And yeah, I know, this definition of OOP-ness sort of flies in the face of decades of things people have said about OOP. Objects, under this definition of OOP, don't obey any of the SOLID principles. They're slippery; unpredictable; "alive." But this was the original definition! It's the one Smalltalk fits; it's the one LambdaMOO fits; it's what is meant by referring to Javascript as object-oriented. It's a shame we have this collision between meanings; having two separate words for "OOP like Smalltalk" and "OOP like Java" would have saved language-designers all a world of arguments from people who try to propose extensions to a language in one category, assuming it's in the other.
- vonkow 9y agoErlang is an amazing language for learning how to effectively use OOP principles. I feel like anyone who works primarily with class-based languages owes it to themselves to write a few server-like modules in Erlang even if they never touch the language again.
- yellowapple 9y agoPerl is also another interesting language for learning the low-level details of OOP, since its default set of OOP features (as of Perl 5; this all changes in Perl 6) involves manually "blessing" some data structure into an object. Objects are thus just "blessed" data structures (and not just hash tables, either, though those are the most common), and methods are just subroutines that accept their object as their first argument. Between Perl and Erlang, I think I've ended up learning way more about real object orientation than I did from my dabbling in C++ and Java.
- cperciva 9y agoThey're both wrong. Closures and Objects are both structures with function pointers and delusions of grandeur.
- Cieplak 9y agoColin, Curious if this opinion extends to something like an Erlang process or a BSD thread. Thanks btw for all your FreeBSD work :))
- cperciva 9y agoI don't know enough about the implementation of Erlang to answer that question.
- Cieplak 9y agoI'm sorry to dissect your quote, which is hall-of-fame-worthy, guess I'm just curious if it's always turtles all the way down, or if there is a "right" level of leak-proof abstraction. I imagine that every abstraction leaks in certain contexts, but maybe certain ones have stuck just because they're "good-enough" for common cases. I suppose I brought up processes and threads since they might provide for a level of parallelism that raw objects or closures lack, without an OS or runtime supporting them.
- taneq 9y agoWhich is funny because I could never get anyone, when I first started programming in C and everyone was raving about how great 'object oriented programming' was, to tell me what 'an object' actually was. When I started learning C++ I was somewhat disappointed to find out an object was just a struct with some functions attached. (It's kind of like when people told me about "user defined data types" and I couldn't wrap my head around how you could create a new data type with all its associated syntax. Then when I learned that they were just bundles of existing data types, though 'oh is that all?')
- abritinthebay 9y ago
- zumatic 9y ago'Qc Na responded by hitting Anton with his stick, saying "When will you learn? Closures are a poor man's object." At that moment, Anton became enlightened.' The cruel tutelage of Qc Na.
- antonvs 9y agoThe Zen Slap: "If you read through enough Zen koans, you’re quickly struck, as it were, by the number of those oblique lessons that end with a student getting slapped. It may be delivered by hand, stick, sandal or goat bite, but the slap, painful as it might be, is an act of compassion, a wordless reminder of the things we know but can never seem to remember or practice for very long." -- http://www.siliconbeat.com/2008/10/09/of-course-if-theres-no-enlightenment-a-zen-slap-just-hurts/ http://www.siliconbeat.com/2008/10/09/of-course-if-theres-no...
- dottrap 9y agoThe TV show NCIS borrows from this philosophy. Gibbs always smacks the back of his underling's heads when they do something stupid. But if he doesn't, they worry he has given up on them. They also made it a point saying that smacking the front of the head would be offensive.
- SomeCallMeTim 9y agoBoth are valuable under the right circumstances. A language that supports both is stronger than a language that only supports one or the other. In that respect, I think the koan is perfect. :)
- runeks 9y agoWhat does "stronger" mean in this context? According to this definition, assembly is stronger than Haskell, because it contains stateful objects (registers), whereas a state-containing object cannot be present in Haskell code, although one may appear when you run the code in question.
- SomeCallMeTim 9y agoStronger means higher leverage for the typical programmer. Assembly language isn't very high leverage (I say that having written several games professionally in entirely assembly language). Assembly language also doesn't natively support either closures or objects, except in the sense that of course you can build them from first principles, Turing Completeness and all that. Until someone proves to me that Haskell can be used in a reasonable way by a "typical" programmer, I simply won't think about pure-functional when I make general statements about languages. I fully admit this may be a hole in my brain, but I don't understand how to break down the problems I face on a daily basis into Haskell (or any pure-functional language), so I just talk about the universe of languages that are not pure-functional. I've looked at Haskell tutorials and gotten at least hip deep into the language, by the way. I just usually end up dealing with problems that require levels of complexity where FP tools fall down. Your mileage may vary.
- tome 9y ago> whereas a state-containing object cannot be present in Haskell code This is somewhere between "not even wrong" and "false".
- jpfed 9y agoReally understanding this means knowing why a closure is a poor man's poor man's closure.
- white-flame 9y agoWhen you get to my depth, you'll understand that a closure is a poor man's poor man's poor man's object. And it smells like a turtle, for some reason.
- lacampbell 9y agoWhile I've seen various methods in scheme to emulate objects with closures, I haven't seen a good one from the statically typed FP camp (the ML family, Haskell, etc). Meanwhile, objects are able to masquerade as closures very nicely in statically typed OO object systems like Scala and C#. So I lean more towards closure being a poor mans object than vice versa.
- deleted 9y ago[deleted]
- tel 9y agoOCaml, Objective Caml, has a built-in Object system. Objects are immutable which prevents them from carrying identity (though mutation can be opted into to recover this). They're also "just" row-typed records. Classes are a form of constructor function which have inheritance for code sharing purposes. This isn't emulating objects with lambdas, but it is a well-articulated idea of what objects in a pure-ish FP setting might look like. There have been many attempts to bring various forms of "object" into Haskell. It tends to be either a very simple or a very messy affair. On the messy side, strict purity blows up object identity again and there are only very messy ways to recover it. This makes most OO such a pain that you're more likely to factorize it into smaller pure components which appear more functional. On the simple side, corecursive patterns are distinctly "object like" and are used all the time. Laziness gives you infinite data structures which, in effect, give you a limited form of mutability which can be incredibly helpful while still be easy to reason about. --- Finally, I think that practically there is a strong case to be made for introducing actor-like objects into Haskell. It has a great runtime and exception system to support it, and it would allow a bit more structuring "at the highest level" of application architecture. Today these are often managed as imperative programs (built an IO chunk, execute it). This tends to work, but Erlang did it better.
- lacampbell 9y agoOCaml, Objective Caml, has a built-in Object system. Objects are immutable which prevents them from carrying identity (though mutation can be opted into to recover this). They're also "just" row-typed records. Classes are a form of constructor function which have inheritance for code sharing purposes. This isn't emulating objects with lambdas, but it is a well-articulated idea of what objects in a pure-ish FP setting might look like. Ocamls objects are great. I'm familiar with them and a big fan. I wish there was a whole language based around them, ie everything is an ocaml style object. That having been said, they do feel odd and out of place in Ocaml, and people don't seem to use them much.
- lngnmn 9y agoClosure is more fundamental though. Add a structure (define an ADT) to a closure and you will have an object. The classic example is a closure with message-passing, which was the original concept in Smalltalk.
- lngnmn 9y agoWell, it seems that having a structure is the most fundamental principle. While in reality structures formed and maintained according to the laws of physics, in CS we have to explicitly define the rules, which is what Abstract Data Types (which define Interfaces) are. Protocols and Interfaces are mere generalizations of ADTs. There are, however, at least some "laws of the universe" in CS. These are Interrupts (the only way to do I/O right), specialized Syscalls, etc. One should respect them.
- ganfortran 9y agoAren't they both just bundle of function objects + states?
- ridiculous_fish 9y agoIn Midori, objects are capabilities. This is possible because type safety is enforced and an object's type cannot be forged. It's easy to imagine a capabilities system built around closures. For example, a capability to send on a socket could be an unforgeable closure that performs that sending. Are there any systems that work this way?
- andrewflnr 9y agoMore details on Midori: http://joeduffyblog.com/2015/11/03/blogging-about-midori/ http://joeduffyblog.com/2015/11/03/blogging-about-midori/ This is the first of several blog posts by one of the authors. I haven't read all of them, but liked what I read so far. Does anyone have other resources with more detail?
- chrisseaton 9y ago> objects are capabilities What does 'a capability' mean? People seem to use the term to mean everything from objects to threads.
- the_why_of_y 9y agoIn this context, it means the object capability model. https://en.wikipedia.org/wiki/Object-capability https://en.wikipedia.org/wiki/Object-capability Which can be used as the foundation of security. https://en.wikipedia.org/wiki/Capability-based_security https://en.wikipedia.org/wiki/Capability-based_security Relevant application in Midori: http://joeduffyblog.com/2015/11/10/objects-as-secure-capabilities/ http://joeduffyblog.com/2015/11/10/objects-as-secure-capabil...
- humanrebar 9y agoAs a counterpoint, people would make poor-man objects in ancient Fortran by using array indices instead of addresses to reference the object. And you could use integers and lots of conditionals to represent polymorphism. You'd have to ban all sorts of things to lock out developers from making their own custom object systems.
- amasad 9y agoI'm curious what's the history behind the use of Zen koans in computing? There seems to be a lot of that. Maybe because in programming there, often, isn't a clear right or wrong (like in this case) so it pays to structure the problem/solution as a story to make people "feel" rather than reason.
- noiv 9y agoIf I had to guess, I'd say it all started with GEB by Douglas Hofstadter. It very well explains aspects of symbol manipulation using koans. However, there might be another precursor.
- dllthomas 9y agoESR attributes the invention of the "AI koan" to Danny Hillis "while a student at MIT". Hillis graduated in 1978. GEB was published in 1979. https://en.wikipedia.org/wiki/Hacker_koan https://en.wikipedia.org/wiki/Hacker_koan https://en.wikipedia.org/wiki/Danny_Hillis https://en.wikipedia.org/wiki/Danny_Hillis https://en.wikipedia.org/wiki/G%C3%B6del,_Escher,_Bach https://en.wikipedia.org/wiki/G%C3%B6del,_Escher,_Bach
- humanrebar 9y ago> Maybe because in programming there, often, isn't a clear right or wrong (like in this case) so it pays to structure the problem/solution as a story to make people "feel" rather than reason. This is pretty close to my thoughts. Though I would add that engineering often lets you solve a problem in several ways. In that way, there's no one "right answer". The exercise, instead, is to consider trade-offs. Koans (1) draw a bright line under the tension between competing but valid ideas. (1) OK. Non-ironic koans. I consider the git koans to be mostly about poking fun at git's inconsistent porcelain.
- hdon 9y agoI actually studied carefully the Koan from the Japanese Buddhist traditions a few years ago. I can't tell you much about Japanese Buddhism, but I can tell you a lot about the Koan. Not all Koan share all the same themes with each other, but for my money, a contemporary Koan really should try to embody all of a few important themes that I find repeated throughout the Koan. A lot of non-experts seem to want to emphasize how often someone is struck, but for me I think this comes from a one of the most interesting themes in the Koan: a sense of humor about failing to attain enlightenment. When a person attains enlightenment in the Koan, it is often at the moment when they are forced to acknowledge the duality of two opposing views, as if in the fleeting moment between seeing Rubin's Vase or the faces in its negative spaces. In that sense, at least, it is a perfect Koan.
- tempodox 9y agoSince objects are just specialized applications of closures, it's not too surprising that you can reconstruct the basic idea (closure) from its application (object).
- Ace17 9y agoOne word: inversion of control.
- white-flame 9y agoMultiple closures can overlap in their referencing of some set of variables. Objects can't really do this, so they're not that similar. Only the most simplistic application of closures (one unique set of instantiated variable slots per closure) matches object encapsulation.
- dllthomas 9y agoObjects containing references can share referents.
- white-flame 9y agoThat's a feature of the references, not of objects. The sharing and overlapping of data slots is however an inherent feature of closures.
- dllthomas 9y ago> That's a feature of the references, not of objects. That doesn't seem like an unreasonable way to draw the lines. > The sharing and overlapping of data slots is however an inherent feature of closures. Is it? When defining a lambda in C++ you can choose for things to be captured by copy rather than reference. I agree with saying that a function that captures nothing is "not a closure", but I don't know that I'd say the same about something that captures only by copy. Would you say that a language that only supported capture-by-copy "lacks closures"?
- white-flame 9y ago> That doesn't seem like an unreasonable way to draw the lines. It's a turing tarpit otherwise, after all. Regarding C++, I've not used the newer versions, but using that feature to capture only copies would match what I said upstream, as a limited usage of closures that would happen to match objects, while closures themselves can do more. Capturing by copy also means that it's instantiating a new closure variable slot specific to the copy, and is no longer closing over the original variable, so it's an additional step on top of the fundamental closure mechanism which would by nature be agnostic to private vs shared variable slots.