Thursday, February 8, 2007

What's wrong with the For Loop, Revisited

I got a little bit of heat on yesterday's post on closures vs. for loops.

First, Cale pointed out that my expansion of foldl wasn't exactly right. Actually, it was a valid expansion of foldl', not foldl. For the purposes of that discussion, I wanted to focus on the concept of left and right folds, not the specifics lazy vs. strict evaluation. If you look at the ghc library docs, you'll find over a dozen variations on the theme of a "fold".

Cale, as always, was prompt, elucidating, helpful and correct. Thanks, Cale!


A few readers also pointed out that my discussion of the power of closures didn't include a single closure. Funny, that. Strictly speaking, curried functions, anonymous functions and sections aren't closures per se. Although I was trying to explain closures, I wound up explaining higher order functions instead.

At one point, I thought about posting a pedantic rebuttal, but really, that doesn't help anyone. So let's call this a fair cop and move on.

Closures are like a gold pass -- if a language provides both first class functions and closures, then it enables higher order programming. Pascal offers neither, and I remember writing sort and search code for every little type I wrote in my data structures class. C, for example, doesn't quite provide first class functions, and it certainly doesn't provide closures. But C does provide function pointers, which can approximate higher order programming in certain limited scenarios. I remember how wonderful it felt when I realized that library functions like qsort and bsearch meant I didn't need to write (and debug) nearly as many sort and search functions anymore.

But I promised you a closure, so here's a function with a closure:
main = do out <- open "out.txt" WriteMode
mapM_ (\i -> hPutStrLn out $ show i) [1..10]
Or, more idiomatically:
main = do out <- open "out.txt" WriteMode
mapM_ (hPutStrLn out . show) [1..10]
Interestingly, here's a different version of that same function that sends output to standard out. Note how similar this code is to the previous example:
main = mapM_ (putStrLn . show) [1..10]
The key here isn't about functions that have bindings to their outer environments. It's about higher order programming. And I should have said that yesterday.

Turns out that if you take some random programming language, add closures and first class functions, you can start to do some pretty amazing things.

Maybe the debate about adding closures to Java isn't really about adding closures to Java. Maybe it's about bringing higher order programming to Java, where the most vocal part of the debate involves complaining about the proposed syntax, or questioning the value making a large language definition even larger..

4 comments:

Don Stewart said...

Note that

PutStrLn h . show

is

hPrint h

Anonymous said...

Hi --

I _really_ don't mean to be Evil but I am sort of amused by how it's been difficult for you to come up with a simple, clear, example regarding closures.
I wonder if it's you or is iit Haskell (Haskell is not really my cup of tea).

Anonymous said...

Um, I can give a few examples of closures coming in handy.

Data mining: clustering algorithms center around the idea of a distance function. Clustering algorithms work on an arbitrary metric function. The distance function is a parameter to the procedure.

In C, you might have a function pointer as a parameter. In Java, you would probably have a class that handles all the clustering stuff and the distance function would be a member method. Then to use a different function you would extend the base class and add your own function. This would entail making a new class and a new file. If higher-order programming were allowed, it would simply be a matter of a different parameter call. This makes experimenting with different functions a lot easier.

Most optimization metaheuristics (genetic algorithms and the like) operate on an arbitrary fitness function and again the same argument applies.

A purely functional, lazy language is not good for optimization/numerical code, though.

And I think for loops are convenient, god dammit.

sigfpe said...

> A purely functional, lazy language is not good for optimization/numerical code, though.

isn't it?