Caging the Effects Monster
Simon Peyton Jones gave an outstanding keynote on functional programming at ACCU 2008. A language researcher at Microsoft in Cambridge, he’s perhaps best known as the man behind the Haskell programming language and GHC, the leading Haskell compiler. He also happens to be a superb presenter who positively exudes enthusiasm. Not many compiler writers connect so well with an audience.
I’m a huge fan of a functional programming style, by which I mean I like first class functions, higher order functions, anonymous functions, list comprehensions, closures and so on. In the languages I use most often, C++, Python and Shell, functional programming is a central paradigm — every bit as useful and important as object-oriented and procedural programming.
For Haskell and other pure functional languages it’s not just about style. Simon Peyton Jones’ central argument was that programming will increasingly be about the control of side-effects: to write correct programs, side-effects must be contained. A pure function has no side-effects. Thus you can be sure what it does. You can even prove it correct. A pure program built entirely from pure functions is equally correct but, sadly, of limited use. Haskell’s type system distinguishes pure functions from impure ones — the ones which change the world. If you minimise and contain impurity, you’re winning!
It’s certainly a compelling argument. In the languages I use from day to day, as already mentioned, I favour a functional style. I also adopt value-based programming (sometimes known as single assignment programming — once you’ve set something, you don’t change it) as far as possible: it’s easier to figure out what a program does, and you can avoid a whole class of defects. Simon Peyton Jones describes my approach as “Plan A”, the mainstream route towards writing programs which are both useful and safe. He promotes “Plan B”, a more radical approach, where “useless” languages like Haskell find ways to change the world.
Strong Static Typing
Static type checking is central to Haskell and the language comes with a rich type system allowing static analysis and compile-time checking. It’s the type system which determines, rigorously, which parts of a program have side-effects. Unlike C++, the type system doesn’t seem to get in the way. A lot more is inferred. In fact, some type declarations are unnecessary since the compiler could deduce them from the code — but in Haskell type declarations, at least for module exports, are seen as a good thing.
I found this interesting. Although I like dynamic languages I also like strong typing (yes, you can have both). In theory, static strong typing would be best of all; you eliminate the bug before you run the code. My experience with languages like C++ and Java have caused me to kick against this theory. The C++ type system just seems to get in your way: by the time you’ve wrestled the compiler into submission and got a program you can actually run you’ve worked so hard you feel like it must be good, when actually you’d have been better off running, testing, revising the code in an iterative manner. You’d certainly have had more fun! Haskell, though, may cause me to revise my opinion once again. It may indeed offer the benefits of strong static typing without the baggage.
A Taste of Haskell
In the afternoon I attended Simon Peyton Jones’ “Taste of Haskell” tutorial, which went into Haskell in more depth. The room was packed and people had to be turned away. Although I have written and read some Haskell I know very little about it, and three hours training with the world expert on the subject haven’t really altered that. Nonetheless, I’d like to record my first impressions here. They may all turn out to be wrong!
I still maintain Haskell could do with proper characters for arrows, lambda, forall, function composition and so on. And guess what: GHC does indeed support Unicode, so you can write a program which uses ∀ instead of
forall, for example. All you have to do is figure out how to input these characters.
- It’s not a minimal language, unlike, say, Scheme; there’s a lot of syntactic sugar, a deliberate decision, but nonetheless a concern. It will take a while before I get used to the syntax even if the concepts seem clear.
- Most (possibly all?) functions take a single argument; so a function taking two arguments is actually a one-argument function which returns another one-argument function.
- You can write your own infix operators and set their precedence.
- Don’t be frightened by Monads. They should really be called “warm fluffy things”.
- Haskell is, by default, lazily evaluated. So it elegantly solves all the stream-based puzzles I’ve been posing on this site. What was new to me was the suggestion that lazy evaluation somehow removes the need for Lisp-style macros. I wonder what’s meant by this?
- Perhaps Haskell remains a vehicle for programming language research as well as for practical application. GHC provides many extensions to the Haskell 98 standard, to the extent that some extensions have effectively become standard.
- The best book about Haskell hasn’t been finished yet. It’s called “Real World Haskell” and it’s being written collaboratively.