raganwald
(This is a snapshot of my old weblog. New posts and selected republished essays can be found at raganwald.com.)

Friday, February 02, 2007
  HOF or OOP? Yes!


It’s interesting how the brain works: I wrote an essay that I thought was an explanation of what first class functions and closures are, and why some people would like to see them added to the Kitchen Sink of Languages.

Then I read From Functional to Object-Oriented Programming, which suggests that my essay seems to be making a general statement about Functional Programming as an advance over Object Oriented Programming.1

Nothing could be further from my intention or my opinion. Smalltalk, for example, has a clean and powerful syntax for first-class functions. And those first-class functions are objects, as is everything in Smalltalk. I am tempted to rewrite my essay using Smalltalk: it seems that using Ruby, another language where everything is an object, is not making this point strongly enough. In my mind, it is easy to use languages that provide the OO paradigm as well as the first-class function paradigm. At the same time.

What I actually said

So what I actually said is: “Here are these things called first-class functions. This “thing” is where functions are exactly the same kind of thing as everything else in a language. This is useful, here is why.” If functions are first class, functions aren’t magically more special, nor are they second-class citizens in a language (like those brain-damaged primitives in crippled languages). If a language is an OO language, this implies that first-class functions in such a language are objects and that programming with them is OO programming.

You’ll notice that I am not using the words “Functional Programming.” That’s because I’m not talking about Functional Programming. I’m talking about closures and first-class functions. I imagine that these things are present in all contemporary Functional Programming Languages, but that has nothing to do with my essay about the value of having first-class functions in an OO language: Smalltalk has had them in an OO language for nearly thirty years, and while it wasn’t the first OO language, it is the canonical definition of class-based OO.

That’s all I said. If you want to debate the merits of pure functional programming languages like Haskell against OO languages like Ruby, you have to take that up with someone like Tom or Eric.

What I don’t understand

I don’t understand the author’s objection to higher-order functional programming. He(?) says something about higher-order functions and type inference as being bad. I don’t get the argument. It seems like he is saying that the constructs are somehow too deeply nested, that it is too easy to make mistakes. I may not understand him correctly, for I have trouble seeing how this is different from having data structures with a thicket of HAS-A relationships.

Somehow we program just fine in business with complex data schema, and we manage to keep track of things just fine. Strong typing and compiler support helps. So does IDE support. So does drawing diagrams. How is “a function taking an integer and a function taking two integers, returning a function taking a list of integers and returning an integer” somehow more complex than “a record containing an integer and that has many records containing an integer”?

The argument seems to be (and I am open to correction, since what I think he’s saying is so obvious that I worry I’m misunderstanding it) that naming things makes all the difference, as in “a customer record with an ID has many Sales Records, each of which has an ID.” That does sound easier to understand.

But of course, nothing stops you from naming things if you have first-class functions in a type checked OO language, does it? I am not a practitioner, but I have been working my way through The Little MLer, and it is part of the ML programming paradigm to name first-class functional types for precisely the reasons the author seems to advocate.

Anonymity considered harmful?

So perhaps the objection is to having too many anonymous things, to having too many values without names. There is certainly an appropriate balance to be sought. One language designer seems to dislike anonymous functions, to the point where his language won’t let you have any that won’t fit on a single line. He has an opinionated language.

I have seen similar arguments with non-functions. That control flow branches should not be nested too deeply (you know, case and if and the evil ternary operator). I often use the “extract variable” refactoring to simplify expressions and make them more readable. Naming a part of an expression provides a certain level of abstraction that improves comprehension.

Types and expressions involving functions are no different, and if that’s what the author is saying I endorse his perspective.

That statement, however, is somewhat orthogonal to whether first-class functions should exist in a language. As long as you can name them, you have the tool to write comprehensible code. If your language has types, then you ought to be able to name types involving first-class functions. As long as you can name functional values and types, you can apply all the same style guidelines to first-class functions that you apply to record types.

In Conclusion

First-class functions are a natural fit with OO, as evidenced by their presence in OO languages that aren’t glorified PDP-11 assemblers with some OO stuff bolted on the side by people with very little OO and/or GUI programming experience. First-class functions can be used to write clean and legible code, using all of the same techniques we use for writing clean and legible code with records, objects, and other types.

From Functional to Object-Oriented Programming has some interesting points about FP and OOP and their historical context. This has nothing to do with my original post, but I think those words are worth reading and considering. The post raises some caveats about complex and anonymous first-class functions that are obviously sensible: we have noticed the same things with other forms of expression in programming.



  1. I will come clean here: when I think of OO, I am not thinking of C++ or Java, especially as they are practised in business. I’m thinking of languages like Self, Actor, and Smalltalk.

Labels:

 

Comments on “HOF or OOP? Yes!:
Well, you're welcome to debate the relative merits of functional programming and Ruby with me, but I'll argue both sides. :-)

More seriously, there are some cases where OO is the right answer, and where functional programming by itself will make your code nasty.

Exhibit A: On our last big Scheme project, we had a number of programmers write GUI components using either an OO style or a functional style. The components written in an OO style were nicely reusable, but the ones written in a classical Scheme style generally turned into unextensible balls of hair.

At the moment, I'm rewriting our low-level OO framework for Scheme to encourage a more Rubyish style on our next project.

Now, I'll happily agree that OO is much nicer with decent support for functional programming. But this is one of those peanut butter/chocolate things: You're better off having both, if at all possible.

Or to look at it from the other direction, there are some designs which can be expressed elegantly in O'Haskell that would be downright ugly in regular Haskell.
 
I think people have a problem with first-class functions because they are so wierd compared to the standard OO stuff. OO has a direct parallel with real world stuff. Functions in general don't have a concrete parallel. If you have studied a lot of math, they are probably easier. If you don't get first-class functions, you won't get closures.

I remember reading about both of them for the first time about a year ago. It took me a while to get what they were about, and how they were different than other constructs. Once I got it, I realized that I had been using both for years programming javascript.

As a corralary, I think that's why some people have trouble with JS: to use it efficiently, you need FCFs and Closures.

About a month into my current job, I had a code review, and the reveiwers seemed totally baffled by something like "var foo = function(bar) {...}".
 
OO has a direct parallel with real world stuff. Functions in general don't have a concrete parallel.

Except real-world stuff has functional thingies like transactions and the Command Pattern.

If someone can understand the idea of a list of commands being used to manage the Undo/Redo stack of a desktop application, surely they can understand the idea of a list of functions?
 
One small thing, I like your blog, but please stop saying SmallTalk. It's one word, Smalltalk.
 
three words: (Anonymous) inner class.
 
Reinier:

Almost. The closing over part isn't handled well by the current behaviour of anonymous inner classes.

That being said, the original post talked about the need for a cleaner syntax. I'm very happy with the idea that first-class functions in Java could be syntactic sugar for anonymous inner classes plus some extensions so you can capture local variables and parameters.

This would be parallel to enum, which is really syntactic sugar for the old "Type Safe Enumeration" pattern.
 
From the article linked to: "The promise of OO was to replace the dazzling complexity (and intellectual challenge) of functional programming with something more manageable."

Really? I think OO as practiced in the industry is way too complex. I find Java and C++ to be quite intellectually challenging, whereas I can understand Lisp and F# just fine.
 
Wow! You remember Actor.
 
You remember Actor.

But I've forgotten NewtonScript!
 
If someone can understand the idea of a list of commands being used to manage the Undo/Redo stack of a desktop application, surely they can understand the idea of a list of functions?

Except that they don't (or don't want to try). With OO, there is alway something being acted on. Obviously, functions act on things also, but it isn't so blatant.

An likewise, assembly obviously acts on things also, but again even let blatant.

That being said, I think alot of the trouble stems from Java Schools. I remember my Java classes at University fo Wisconsin being boringly easy -- granted I did take a year of C++ in High School. When you ask someone to move out of their comfort zone, and learn a new language and paradigm, some people get scared. I think this is especially true for a language like Java which tries to abstract the wrong things.
 
Reginald wrote: If someone can understand the idea of a list of commands being used to manage the Undo/Redo stack of a desktop application, surely they can understand the idea of a list of functions?

There's actually a really clever way to manage undo/redo in a purely functional language: keep around old copies of your data structures, allowing them to share structure with newer versions. Since no code can modify the data in place (it's all effectively "const"), it's safe to overlap different versions in this way.

For a really excellent book on this subject, see Purely Functional Data Structures.

So in an object oriented language, you use lists of commands to implement undo, but in a functional language, you use plain old data!
 
The real problem with higher order function is that it encourages, and sometimes enforces creating anonymous objects, which is generally bad. You should always name your types, your objects and yes, your methods too. Any anonymous object, of any kind constitutes a possible threat to code maintenance unless you have foresight and KNOW that this never needs to be named. (The only possible exception are numbers, because infix calculations are so readable and commonly understood that it's needless to name intermediate result.)

That's why I not only reject HOF, but anonymous classes and unnamed objects as well.
 
You should always...
Any anonymous object, of any kind constitutes a possible threat...

Seems a little on the side of Bondage and Discipline, but if this is working for you, I say carry on with enthusiasm.

Have you considered that code is not cast in stone? It is always possible to write something with no idea whatsoever if it will need a name later, and then name it when you find out you need a name?

This is the basis of DRY, to me. If something is only used once, it is least in need of a name. Especially if it is small.

When we find out later that we need to re-use the same thing again, we can name it and share it.


In any event, I am not at all convinced that a desire to name everything is in opposition to HOF.

Can't you use maps and generators and list comprehensions and all sorts of other HO tools to create things that you name?
 
Yeah, how you name things and HOF are othorgonal. Python vs. Perl shows that pretty well; both can do it, Perl is much friendlier if you don't like naming things.

I find I've come to appreciate the Python way of doing things when it comes to working with the code of others. It's extremely rare for me to encounter even a snippet of code that I really can't put a name on, and that often constitutes a code smell anyhow. (In the original sense of "code smell"; it isn't a priori a problem, but it probably indicates you're still fuzzy on exactly what's going on, which happens.)

If you're writing code on your own, go to town. But I really like naming things, even when I'm in pure functional. I really like the ability to "read" off programming statements, even if the grammar is in a different order than English.
 
Hi, I've tried to clarify some points from my original post in a follow-up on my blog - it just got a bit too long to post here. I believe my main point, don't get lost in abstraction, and the value of naming, were already reformulated excellently by Reg. Still, to me the value and use cases for closures in the OO paradigm seem to be different than in the functional paradigm... (extended version)
 
Hi,

Every time when I tell my opinion on programming, I am insulted with "bondage and discipline". If I create my blog, I guess I should name it "BDSM Programming" or similarly. :)

But seriously: when you know how to name a thing, you should do it, and by not naming you're doing harm to code maintainers. If you don't know how to name a thing, that means you're in grave need of refactoring.

Everything that may be manifest, should be.

albert
 
Albert:

Every time when I tell my opinion on programming, I am insulted with "bondage and discipline".

It's not meant as an insult, just a way of making a friendly joke in the style Ha ha, only serious.

The serious part is in reference to what sound like absolute, inflexible rules. This is not a comment in agreement of disagreement with the rules themselves, just an observation about the style of development.

I know how to name many things. I do so when I need to use the thing in more than one place, or when it is easier to read as a series of lemmas rather than a single large expression.

I have a feeling that my style has been influenced by Agile development and by refactoring (whether automated or by hand).

I am not paranoid about colleagues coming after me and moving things around or naming things if they need. I don't try to guess that they need, I try to make things malleable so they can be refashioned.

That's probably a post in itself. I think it's related to YAGNI. Not in the sense of hacking without forethought, but in the sense of building code that is easy to change.
 
Axel:

Thanks, great follow-up!
 
There's an excellent presentation by Luca Cardelli called Everything is an object where he shows that an object calculus can be more expressive and comprehensive than the lambda calculus, concluding: "Hence, objects are more basic than procedures". What I find interesting is that he backs this claim by showing that it's cumbersome to translate the typed object calculus to the typed lambda calculus.

A language that is based in a similar object calculus is Obliq, also by Cardelli.
 
The above post on Obliq is mine. Blogger seems to function strangely without javascript enabled.
 
OK, so you joked at me, I accept this, but others probably do not joke :-) But this has another side too: the bondage part left out, the discipline is most important.

But it's hard to self impose the discipline for a programmer, I am one so I know it. That's why I prefer to rely on languages and tools to guarantee that programs I create (along with the team) have no unnamed objects, no unnamed classes. They do enforce the discipline and when this discipline is enforced, the names they give objects are meaningful too.

YAGNI is a good rule for avoiding overdesigns, we do use it too, but it's not good for avoiding "overnaming" (if such a thing existed) because I name things not for this myself sitting at the keyboard NOW, but for that myself who will try and understand the code a year later, or even for other people trying. YAGNI is so good because by demanding to keep things simple makes it easy to add needed things later, when they are really needed. But there is no such thing as "easily naming objects later": it will just always be very hard. That's why you should name things as you write, and not leave it for ever.

(And, this makes static manifest typing an obvious thing you want to have in your language.)
 
Albert, consider loop bodies. Should they be named? After all, if you think about it, a block in a language like C or Java isn't unlike an anonymous function.

But, I think you'll agree, it'd make no sense to give them names.
 
the bondage part left out, the discipline is most important.

Well, it's just a joke, but for the sake of stretching our imagination, let's think of these definitions:

Bondage: restricting programmers' freedom by imposing limitations on what their language or tools permit them to do.

Discipline: restricting programmers' freedom through social methods such as codifying practices.

B&D apply to all sorts of cultures. For example, if an "agile" shop sets up a continuous integration server that rejects any code that breaks the build, you have a form of bondage.

If an "agile" shop encourages "YAGNI" through training, code review, and refactoring to YAGNI, you have discipline.

So...

I would say that both really do have their place, and the important thing is to find the balance that works properly for your group and your project.

Pax!
 
So how would you tell about enforcing codifying practices with a tool? :-) Is it bondage or discipline?

ps. I am not a native english speaker and when I try to find what this "bondage and discipline" is using Google search... well, guess why I don't get this joke :D
 
ps. I am not a native english speaker and when I try to find what this "bondage and discipline" is using Google search... well, guess why I don't get this joke :D

Ouch. As independent to this conversation I can guarantee the tone was much lighter than was implied by what I'm sure came up in your google search :).
 




<< Home
Reg Braithwaite


Recent Writing
Homoiconic Technical Writing / raganwald.posterous.com

Books
What I‘ve Learned From Failure / Kestrels, Quirky Birds, and Hopeless Egocentricity

Share
rewrite_rails / andand / unfold.rb / string_to_proc.rb / dsl_and_let.rb / comprehension.rb / lazy_lists.rb

Beauty
IS-STRICTLY-EQUIVALENT-TO-A / Spaghetti-Western Coding / Golf is a good program spoiled / Programming conventions as signals / Not all functions should be object methods

The Not So Big Software Design / Writing programs for people to read / Why Why Functional Programming Matters Matters / But Y would I want to do a thing like this?

Work
The single most important thing you must do to improve your programming career / The Naïve Approach to Hiring People / No Disrespect / Take control of your interview / Three tips for getting a job through a recruiter / My favourite interview question

Management
Exception Handling in Software Development / What if powerful languages and idioms only work for small teams? / Bricks / Which theory fits the evidence? / Still failing, still learning / What I’ve learned from failure

Notation
The unary ampersand in Ruby / (1..100).inject(&:+) / The challenge of teaching yourself a programming language / The significance of the meta-circular interpreter / Block-Structured Javascript / Haskell, Ruby and Infinity / Closures and Higher-Order Functions

Opinion
Why Apple is more expensive than Amazon / Why we are the biggest obstacles to our own growth / Is software the documentation of business process mistakes? / We have lost control of the apparatus / What I’ve Learned From Sales I, II, III

Whimsey
The Narcissism of Small Code Differences / Billy Martin’s Technique for Managing his Manager / Three stories about The Tao / Programming Language Stories / Why You Need a Degree to Work For BigCo

History
06/04 / 07/04 / 08/04 / 09/04 / 10/04 / 11/04 / 12/04 / 01/05 / 02/05 / 03/05 / 04/05 / 06/05 / 07/05 / 08/05 / 09/05 / 10/05 / 11/05 / 01/06 / 02/06 / 03/06 / 04/06 / 05/06 / 06/06 / 07/06 / 08/06 / 09/06 / 10/06 / 11/06 / 12/06 / 01/07 / 02/07 / 03/07 / 04/07 / 05/07 / 06/07 / 07/07 / 08/07 / 09/07 / 10/07 / 11/07 / 12/07 / 01/08 / 02/08 / 03/08 / 04/08 / 05/08 / 06/08 / 07/08 /