One More on Haskell
Posted by Daniel Lyons Fri, 28 Sep 2007 09:11:00 GMT
My faith in Haskell has wavered somewhat lately, and all because of Reddit. Essentially, three things:
- Haskell’s fixed-point combinator,
y f = f (y f). - The ramifications of Haskell’s purity on abstraction.
- Haskell’s epidemic academia.
The first is really two problems. One is that the usual definition of the Y-combinator is not itself recursive. Obnoxious. The second problem is that, though it’s beautiful, there are a number of problems that you can’t express in Haskell using the above combinator which you could in an untyped language. This isn’t, apparently, news to anyone but me. I can’t see how to use the above Y-combinator. This is an incredibly academic problem, like most Haskell problems, but it bugs me a little.
The second was elucidated by Peter Van Roy on Lambda the Ultimate’s forum:
“True state lets information pass “underground” between two interfaces, i.e., the information passes without any apparent connection between them. This is because the connection is the shared state, which is shared by the two interfaces yet hidden from the outside. The shared state is a kind of covert information channel: it lets a module pass information to other modules (or to itself in the future) without anybody else seeing it.”
His point has to do with the fact that Haskell’s purity means that in order to get data from some point A to some point B through a number of other modules, each of the intermediate modules will have to carry the information around, even if it doesn’t do anything with it.
I would like to hate that, really, I would. But I can’t, because I programmed Voltaire in a very functional and abstract way in PHP partly because I could count on a handful of global variables passing some state around “underground” between modules that were loosely connected. In particular, Voltaire creates a region context and a template context in which each script is evaluated. The database connection is also shared clandestinely like this. If I were using Haskell, every function in the system would have to take an extra four parameters to get the current region, template, path and database connection, even if it wasn’t going to use it or pass it directly to a child.
It’s easy to denounce. At the same time I feel blameless for having done it, because while the state is “available” to anything beneath, nothing should be changing these variables outside the core. It’s available in a read-only way. Later on I wrote a plugin for rendering templates programmatically, and that involved understanding the inner state, and another plugin for producing lists which also involved understanding the inner state. But languages like Lisp provide interesting semantics for those times when you would want to change the behavior of a global variable safely.
Which brings me back around to Lisp, the language I have paid the least attention to of the four or so that I decided were “safe” so long ago when I started to force myself to use functional programming. And, truly, there are things about Lisp which are hard to love. The principle advantage of Lisp is that it permits you syntactic innovation. It’s certainly the only language that provides it meaningfully (let us not quibble about Lisp/Scheme differences or bizarre languages like Pliant). But doesn’t this seem like the fundamental abstraction of programming languages?
Aren’t we always starting with some problem and selecting a language based on its syntactic abstraction of some part of the problem domain? I mean, I pick Lisp because I want the ability to create my own structures. That comes in handy against every problem domain. But if I want to write a blog, well, Rails makes it a lot easier up front. Maybe I don’t get linguistic abstraction, but the starting abstraction is quite close. Michael chooses REALbasic for application development, I choose Cocoa. Both of us are making certain sacrifices in the name of abstraction. I lose defmacro. He loses the most recent Cocoa innovations. If I pick Lisp for my blog, then I’m sacrificing the fast start. The laziness of later syntactic abstraction had better pay off. The up-front cost is higher. For any given reasonable language, there is going to be a situation in which its abstraction is an up-front benefit that beats the up-front cost of using Lisp.
You see, these languages are compression algorithms. They compress your explanation of how to solve a problem. Like any compression algorithm, there are problems for each language that compress so badly you wind up writing a different language inside the language. This is Greenspun’s law all over again. Lisp wins frequently because it makes it easy to create a sublanguage for a subproblem that compresses it better. Ruby wins frequently because it encompasses most problem domains.
And Haskell wins frequently in academia because academia is interested in representative subproblems rather than complete problems. My experience on the ICFP this past year was that when you give Haskell a pure math problem, it will beat nearly anything else hands-down for both readability and speed. Now sprinkle some I/O on the problem. How about a non-local return? Perhaps a bit of, dare I say it, destructive state? Suddenly you find yourself looking up “monad transformer,” wondering what the hell you got yourself into.
And Justin, G-d bless him, is a Haskell wizard. I can sort of read the code he wrote. My contribution to that code was negligible. I wasn’t much help debugging it. But at the end, we had a performant Haskell version using prominent Haskell performance themes. But it was also 300 lines of code. I remain convinced against any of that pesky proof stuff that an OCaml implementation would have been half the length, more understandable, maintainable and debuggable. I believe Justin came away from the contest with the opposite conclusion: proof, solid proof, that Haskell can be made to attack the kinds of domains that other languages are generally selected for. In other words, his success with Haskell encourages him to use it more, whereas it filled me with doubt.
And today I see on Reddit a link to the paper introducing Haskell Server Pages. Not to the code itself, or the documentation talking about it, or to a page talking about it or some examples, but to an academic paper about it. I write on Reddit that “I have to admit I’m getting a bit tired of seeing things that should be cool Haskell libraries show up as academic papers. It’s as though the whole industry has turned its gaze to Haskell, exasperatedly asking to see something practical, and instead of turning out practical things, the Haskell world turns out academic papers about practical things. “Proof” that Haskell can be used practically is not the same as people using Haskell practically, nor the same as people practically deciding to use Haskell.”
Let’s face it. I really enjoy Haskell in part because of its snobbery, but that’s really a part of what it is. And the posturing about how mind-expanding it is. Haskell is addicted to academia. You’re much more likely to see a paper about some neat library than a neat library. When you go look at the code, it’s untested, only works on one platform, is broken or partially implemented. Academics are not rewarded for having good, useful code. They’re rewarded for writing papers.
Look at the Wash page and tell me who’s going to use this framework. Look at the first four links. Notice these PDFs and PS files are all LaTeX output. What kind of web people would do this? Academic web people.
So here we are. I suppose every language has its vices. I’m particularly drawn to languages that do a lot of posturing, apparently. Beyond that I’m not sure what to conclude. Every language that isn’t pure offal (Java) seems to have a place in the world. It would be better not to get too worked up about it, but it’s probably impossible.
