Languages / Language

1990

Haskell

Lazy, pure and typed: the committee language the others keep borrowing from.

In September 1987, at the Functional Programming Languages and Computer Architecture conference (FPCA) in Portland, Oregon, a group of researchers met to talk about a problem. There were more than a dozen lazy, purely functional languages, all much alike, and having so many was holding all of them back. They agreed to form a committee and design one common language. Its first report, edited by Paul Hudak and Philip Wadler, came out on 1 April 1990. They named it after Haskell B. Curry, the logician whose work underlies much of theirs.

Where it came from

The graph draws this as ML, then Miranda, then Haskell. That is a simplification, and here is the fuller line.

  • ISWIM to SASL. Peter Landin’s ISWIM (1966) was a sketch of a language built on the Lambda Calculus. At St Andrews, David Turner built SASL on its pure core, strict at first in 1972 and lazy by 1976. KRC followed at Kent in the early 1980s. Both were untyped.
  • Miranda (1985). Turner’s next language added the polymorphic types and type inference that ML had proved, and he sold it through his own company, Research Software. By 1987 it ran at 120 sites and was by far the most mature of the lazy languages.
  • Haskell (1990). The committee’s first idea was to start from Miranda. Turner said no, cordially: he wanted one Miranda with no dialects, and asked that the new language be distinct enough not to be confused with his. So they started again, and borrowed freely. Haskell’s look (equations with pattern matching and guards, where clauses, list comprehensions, layout instead of braces) is Miranda’s. Other lazy languages fed in too, Lazy ML from Chalmers among them.

The name

At the committee’s meeting at Yale in January 1988, everyone wrote names on a blackboard: Semla, Vivaldi, Candle, Fun, Curry and more, then took turns crossing out the ones they disliked. “Curry” was the one left. That night two of them saw the puns coming, so the next day it became Haskell instead. The committee wrote to Curry’s widow, Virginia, to ask if she minded, and Hudak later visited her. She came to one of his talks, and her parting remark was that Haskell had never liked the name Haskell.

What made it different

  • Lazy. Nothing is worked out until it is needed. You can define an infinite list and take ten things from it, or write a function that builds far more than any one caller uses. The price is that running time and memory are harder to predict. The classic Haskell bug is the space leak, memory held by work not yet done, and Haskell 1.3 (1996) added seq to force things when you have to.
  • Pure. If you don’t know when something will run, side effects become chaos, so laziness all but forces purity: a function’s result depends only on its arguments. That left the hard question of how a pure program talks to the world. Haskell 1.0 did it with lazy streams of requests and responses, as Miranda had.
  • Monads for I/O. In 1989 Eugenio Moggi used monads, from category theory, to structure the meaning of programming languages. Wadler saw that the same trick could structure ordinary programs, and he and Simon Peyton Jones showed in 1993 how to do I/O that way. A value of type IO a is an action that will do some I/O and produce an a; the type tells you which code touches the world. Monadic I/O, with do notation, became the standard in Haskell 1.3 in May 1996.
  • Type classes. The committee needed a tidy way to let + and == work on many types. Wadler had the idea in a conversation with Joe Fasel, his student Steven Blott worked out the type rules, and their 1989 paper “How to make ad-hoc polymorphism less ad hoc” set it out. The committee adopted it by acclamation, and it became the most distinctive thing in the language.

The committee meant Haskell to be a vehicle for research, so it never stopped changing. Haskell 98 (published in February 1999) was a stable version people could rely on, and Haskell 2010 added a foreign function interface, hierarchical module names and pattern guards. Yearly revisions were meant to follow; none did. In practice the language is now whatever GHC accepts, and GHC names its own language editions, such as GHC2021.

What it gave later languages

The authors of the Haskell history say it is often hard to prove cause and effect, so here is what can be shown, and what is only likeness.

  • Type classes. The Rust Reference names Haskell as the source of Rust’s traits (“typeclasses”, it says). Java’s generics came from GJ, which Wadler co-designed, and its bounded types are close cousins of type classes. Swift’s protocols do a similar job, and Chris Lattner lists Haskell among Swift’s influences, but that is a likeness, not a documented line.
  • Maybe and Either. A value that might be missing is Maybe a; a result that might be an error is Either e a. Rust’s Option and Result, and Gleam’s Result, are the same shape. The idea is older than Haskell: algebraic data types came from Hope and the ML family, ML has its own option type, and Gleam’s announcement names Elm, OCaml and Rust as the inspirations for its types. Call it a shared inheritance, one that many programmers first met in Haskell.
  • Monads and do notation. C#‘s LINQ queries are based on Haskell’s monad comprehensions, put there by Erik Meijer, a Haskell committee member. F#‘s asynchronous workflows are a monad too, written in syntax their designers compare to Haskell’s, and C# adapted that design as async and await. So the await in much of today’s code has a monad underneath it, a grandchild of Moggi’s idea.
  • Laziness. This mostly stayed home. Lazy sequences turn up in Clojure, Raku and Rust’s iterators, but almost no other popular language made laziness the default.

Where it runs

GHC, the Glasgow Haskell Compiler, is the one most people use. Work began at Glasgow in January 1989 with a prototype written in Lazy ML by Kevin Hammond, and then Cordelia Hall, Will Partain and Simon Peyton Jones started GHC proper that autumn, written in Haskell itself. The first beta came out on 1 April 1991 (the date was no accident). GHC compiles to fast native code, and its ghci and runghc run code straight away, which is what the playground uses. In the 1990s many people learnt with Hugs, a small interpreter.

The people

  • Paul Hudak at Yale and Simon Peyton Jones, then at University College London, decided on the way to FPCA in 1987 to call the meeting that started it all. Hudak co-edited the first report.
  • Philip Wadler co-edited it too, came up with type classes, brought monads from semantics into everyday programming, and later co-designed GJ, the basis of Java’s generics.
  • Simon Peyton Jones moved to Glasgow in 1989 and led GHC for decades.
  • John Hughes wrote “Why Functional Programming Matters”, the case for lazy programming, first given as a job talk in 1984 and published in 1989. He co-edited Haskell 98 and, with Koen Claessen, wrote QuickCheck, which tests code against properties with random data.
  • David Turner never joined the committee, but without Miranda, and SASL and KRC before it, there would have been little to build on.

What made them different was that a committee actually worked. Most language committees produce compromises. This one agreed on a few firm principles (lazy, pure, free for anyone to implement) and then let the language keep changing, so that it served as a laboratory as much as a tool. Type classes, the most famous idea in it, were adopted early, unproven, because they were the cleanest answer anyone had.

Landmark open-source projects

  • GHC: the compiler.
  • Pandoc: converts documents between dozens of markup formats.
  • ShellCheck: finds bugs in shell scripts.
  • xmonad: a tiling window manager configured in Haskell.
  • QuickCheck: property-based testing, since copied in many other languages.
  • Pugs, Audrey Tang’s 2005 implementation of Perl 6 in Haskell, which showed Raku could run.

Try it

-- All the primes. Laziness means only the ten we take are ever found.
primes :: [Int]
primes = sieve [2 ..]
  where sieve (p : xs) = p : sieve [x | x <- xs, x `mod` p /= 0]
        sieve []       = []

main :: IO ()
main = print (take 10 primes)

Sources

See also: ML, Lambda Calculus, Pattern Matching, Immutability, State, Gleam.