ONLamp.com
oreilly.comSafari Books Online.Conferences.

advertisement


An Introduction to Haskell, Part 1: Why Haskell

by Adam Turoff
05/24/2007

Let me start by being perfectly clear: if you are a professional programmer, then Haskell is in your future.

In 1987, this statement would have been equally true about Smalltalk. Today, 20 years later, object-oriented programming, class hierarchies, model-view-controller patterns, and other ideas from Smalltalk are now commonplace, even though Smalltalk never saw widespread adoption.

Haskell is in a similar situation today. It is an esoteric language, yet stable and reliable enough for serious work. Perhaps you will learn Haskell, or even use Haskell on a real-world project. Even if you don't, ideas from Haskell are slowly finding their way into mainstream languages like Java, C#, C++, Perl, Python, and Ruby. Haskell is poised to have a strong influence over the next 20 years of programming and language design, just as Smalltalk has had an influence during the last 20 years.

What Is Haskell?

Haskell sounds important, but what makes it so special? The brief definition of Haskell from haskell.org says:

Haskell is a general purpose, purely functional programming language featuring static typing, higher order functions, polymorphism, typeclasses and monadic effects.

If you are an academic or a language designer, these terms may be meaningful. If you are a professional programmer, terms like static typing and polymorphism may sound familiar, even if this definition isn't particularly elucidating.

Fundamentally, Haskell is a functional programming language, which means it is based on a form of lambda calculus. If this sounds intimidating, then fear not, because lambda calculus is not difficult at all.

Lambda calculus is nothing more than a precise set of rules that describe how to evaluate functions. There are two moving parts (function definition and function evaluation), and two building blocks (functions and values). If you're a professional programmer, you already have all the skills you need to understand lambda calculus. For example, in any C-like language, the rule for evaluating this statement is already wired deep into your consciousness:

    printf("%.03f\n", sqrt(incr(1)));

The rule is to evaluate all the arguments to a function first, then evaluate the function with the resulting values. This process is repeated recursively, until an expression yields a single value. In the example above, the innermost function, incr(1) is evaluated to produce the value 2. Next, sqrt(2) is evaluated to produce 1.414.... Finally, printf is evaluated with the two arguments "%.03f\n" and 1.414... to produce some formatted output.

There are a few more key features to lambda calculus, like the ability to create functions that return functions, and the ability to define functions that take functions as arguments. These features make it possible to define small, modular functions, and glue them together into highly specialized functions with minimal effort.

For example, filter is a general purpose function that accepts a testing function (a predicate), a list of values, and returns a new list containing all values where the test passes. The even function is one such test, which determines whether or not a number is divisible by 2. Here is how those two functions are used together in Haskell, to extract the even numbers from a list of values. This example uses ghci, the interactive interpreter provided with the Glorious Glasgow Haskell Compiler (ghc):

    Prelude> filter even [1..10]  -- filter the even numbers out of a list
    [2,4,6,8,10]

If I want to define a function that takes a list of values, and returns only the even values in that list, it would look like this in the interpreter:

    Prelude> let evens = filter even  -- define the function
    Prelude> evens [1..10]            -- filter numbers out of a list
    [2,4,6,8,10]

This example may appear trivial, but it demonstrates a key benefit of working with lambda calculus in a functional programming language--the ability to create specialized functions by merely gluing together a small number of more generalized, pre-existing functions.

Interestingly, this is exactly how shell pipelines work.

Writing the function evens in C is much more complex. First, the output list must be allocated, and to do that, the number of output values must be counted. Next, the values must be copied to the new list, one at a time. This simple expression in Haskell becomes this function in C:

    int *evens(int *in, int count) {
        int matches = 0;
        int i, *out, *ptr;
        
        for (i = 0; i < count; i++) {
            if ((in[i] % 2) == 0) {
                matches++;
            }
        }
        
        out = malloc(sizeof(int) * matches);
        ptr = out;
        
        for (i = 0; i < count; i++) {
            if ((in[i] % 2) == 0) {
                *ptr = in[i];
                ptr++;
            }
        }
        
        return out;
    }

This function defines the combined behavior of the Haskell definition of evens. That is, this block of code is equivalent to the expression filter even in Haskell. If there were a C library function that handled the behavior of filter, this code would be simpler. However, this is kind of solution is sadly idiomatic in C-like languages.

Pages: 1, 2

Next Pagearrow





Sponsored by: