This is part 2 of a series (consisting of at least two posts) comparing imperative programming languages with functional programming languages. Here is Part 1: Context determines meaning. By the way, Haskell is my prototypical functional language because it’s the purest functional language about which I know anything. C# is my prototypical imperative language because I know it pretty well.
It seems that functional algorithms and programs are quite a bit shorter than their imperative counterparts. For example, here is a possible definition of a function to calculate square values in Haskell:
square x = x * x
For sake of comparison, here an equivalent function defined in C#:
static double Square (double x) { return (x * x); }
The Haskell version is less than a third as long as the C# version. As far as I’m concerned, this is a win for Haskell. I much prefer a syntax that does not get in the way of what a program actually does. This is mostly why I gravitated to C# even after several years of nothing but VB. VB syntax may be easier to learn for the beginner, but I find it slows me down because I have to read (or worse write) past a bunch of words before I get to the meaningful part. Of course, C# may be concise compared to VB, but it’s hard to imagine a more concise syntax than is afforded by Haskell.
How can a functional language like Haskell get by with such an economy of syntax? One reason is that Haskell was designed to create functions, first and foremost. Do one thing and do it well, as the saying goes. Another reason is that Haskell is very good about inferring the types of things from other things. You can give things an explicit type in Haskell, but often the only reason to do so is to help the compiler help you when you screw up. For example, Haskell knows that ‘square’ is a function that returns a numeric value. It knows this because it knows that the ‘*’ operator returns a numeric value. It also knows that parameter ‘x’ must be a numeric value, likewise from the definition of ‘*’. In other words, Haskell will not let you do something like this:
y = square "foo"
Of course, it is possible to define a function in Haskell that does not, in its definition, imply a particular return type at all. In such a case, Haskell will define the function in a generic fashion. Think C# generics without all the syntactic fluff. Consider Haskell’s ‘foldl’ (fold left) function:
foldl f z [] = z foldl f z (x:xs) = foldl f (f z x) xs
Basically, ‘foldl’ applies an operation to each item in a list, accumulating the result of the operation along the way. The accumulated value is the return value of ‘foldl’. As of version 3.5, the .NET Frameworks has a function that serves the same purpose: Enumerable.Aggregate. What does it look like? Hang on to your hat…
public static TAccumulate Aggregate( this IEnumerable source, TAccumulate seed, Func func) { if (source == null) { throw Error.ArgumentNull("source"); } if (func == null) { throw Error.ArgumentNull("func"); } TAccumulate local = seed; foreach (TSource local2 in source) { local = func(local, local2); } return local; }
Why is Haskell’s version so much shorter? One obvious answer is Haskell’s penchant for exceedingly short parameter names (something I don’t care for, actually). It’s much more than that, though. In Haskell’s version, there are no explicit types, no explicit generic parameters, and much less ‘punctuation’. Does this mean that Haskell is weakly-typed? Not at all. Haskell’s version is at least as strongly-typed as the .NET version. In fact, considering that Haskell will not let you pass invalid parameters to ‘foldl’, it can be considered to be more strongly typed than the .NET function, and also eliminates the need for parameter validation in the body of the function.
I’m not sure what part 3 of the series will be (or even if there will be a part 3), but I’m considering a post about why IO is a challenge for pure functional languages and how Monads make it much better.
