Wow, if I had known my previous post would be read with such scrutiny, I might have left it to ripe a bit longer. I’m really happy to have helped ignite such a balanced and informed discussion!
Originally I intended to post the following as a comment on Reginald’s blog, but I was unable to keep my prose down to a reasonable size… blogs are not the perfect medium for a discussion, but here we go.
The point I tried to make about type checking is that the type checker has no meaningful way to report typing problems if you don’t give him the names, but just a knot of function types. Similarly, if everything is just a list (as in ideal Lisp), the runtime errors can speak of nothing but cons cells.
Reginald, I’m glad you think that the importance of naming things is “obvious” – some programmers appear to think otherwise. Someone might find the task of making up good names unnecessary, or challenging, so they skip it where possible (especially when working in a haste, especially those with a maths/physics background who have learned to deal with pure structure). I consider this bad practice for software, and therefore would not want a programming language to actively encourage conceptual anonymity.
I probably mistook the point of your original post because you set out to answer the question “why should I care about closures, as Java programmer”, but described closures from a purely functional perspective. I believe Java programmers need to care about closures not because the functional paradigm is great, but because of how closures enrich OO programming.
Haskell and ML are much better at domain modelling than Scheme, because of their type system (which is notably absent in Ruby) We can assume “functional-style” programming in Ruby resembles Scheme more than ML, so that’s what I was relating to, given that your examples were using Ruby.
Closures in Smalltalk were originally not full static closures – they were used mostly for control structures and iteration. The full power of closures (as in Scheme etc.) was only added in a later iteration. Which seems to indicate to me that their full power was not required for the “prime use cases” of OO programming – you could still refactor to class-based objects when you needed more.
The Undo/Redo stack is a perfect example of something that should NOT be done using closures. Because each command is not one, but two closures (one for Redo, one for Undo), so which is your object identity?; a command may have a name you want to present to the user (“Undo move” vs. “Undo delete”); and when your software is supposed to persist a command log, or merge commands executed by different users on the same document, you will need a much deeper analysis (instead of just execution) of the commands. Of course you can try to refactor at some point, but searching for all usages and implementations of “Closure.apply” will not produce very meaningful results.
Transactions are another example of more than just a closure – they’ll have an isolation level, a name, an identifier,… Obtaining these property values certainly requires a broader interface than closure.apply(), or another object to wrap the closure, again splitting up object identity. What transactions do have is a Single Abstract Method – did I mention I like the CICE proposal?
So I actually would like a language that is “opinionated” in the sense that it gives guidance. Would you really want to program in an opinion-less language, if such a thing can be imagined at all? How would two pieces of code in that language every fit together? In the Tycoon libraries, each data structure was implemented three times, once functional, once imperative and once OO. Seems wasteful, doesn’t it? All language designs I like express the opinion that “goto” should be rarely used. Don’t you think? In a similar vein, I would like a language to express that most things ought to be named and related to domain concepts.
Great post!
Just my “two cents”:
“The Undo/Redo stack is a perfect example of something that should NOT be done using closures.”
I’ve never implemented it as such, however you can capture some common identity using lexical scope. You end up re-inventing OOP. I call this “greenspunning.”
SICP is great for learning this kind of thing: it doesn’t say “closures are better because they are powerful enough to express OOP,” it says “you will understand OOP better if you understand how to build it out of more fundamental constructs.”
I mentioned undo/redo to point out that OO is not just modeling things that are nouns in the real world. OO can model relationships, actions, all sorts of non-noun things. Undo and redo are examples. Session beans are examples.
My point about OO and first-class functions is that if you can wrap your mind about using an object to model a command or transaction, you can wrap your mind around using an object to model a function.
The rest of the debate, with respect to the “Kitchen Sink Language,” concerns brevity of notation and access to variables in lexical scope.
Definitely! I wouldn’t know how to explain the full power of the Command Pattern to someone who has never done functional programming. Or explain SwingUtil.invokeLater to someone who does not know about continuation passing style…
This is one of my favourite spars on typing that I can remember. Keep it up.
I particularly like the idea that with OO you don’t really need ‘closures’ in the full sense of the word, just a convenient way to wrap code into an object on the spot. CICE is looking better than ever.