Haskell1 is a most unusual language. At one end it’s a hard-core functional language with impeccable CS credentials, but increasingly I’ve come to use it for simple mathematical calculations too.
I’m not entirely sure how the language manages to fill both roles so well, though it surely demonstrates the designers’ exquisitely good taste. However, I think the following are important:
- Haskell ‘feels’ more like maths than most other languages.
- Haskell is basically lazy,2 which facilitates conveniences like infinite lists.
- Haskell has built-in transparent support3 for bignums.4
- Haskell is blessed with lots of little operators and functions which do useful things. For example, the $ operator avoids lots of brackets. $ roughly means evaluate the right-hand side and continue, and so turns nested expressions into linear ones:
a $ b $ c d = a(b(c d))
. - In ghci5 Haskell has a splendid REPL.6
Besides the language itself of course, it’s helpful to have some good libraries. Happily Hackage7 has lots of good stuff in it, though it can’t match e.g. the CPAN.8
A simple calculation
As an example, suppose we want to know the sum of the factorials of all the primes less than 50. Here’s one way (assuming you’ve installed the primes package9):
$ ghci
GHCi, version 7.4.2: http://www.haskell.org/ghc/ :? for help
Prelude> import Data.Numbers.Primes
Prelude Data.Numbers.Primes> let fac i = product [1..i]
Prelude Data.Numbers.Primes> sum [ fac i | i <- takeWhile (< 50) primes ]
258623301959883784393716899074939573050130131319471976510768
Note: I’ve removed some of ghci’s diagnostics to make things clearer.
Defining variables
Haskell can also play the poor man’s symbolic calculator, here calculating an approximate value of g, the acceleration due to gravity10 at the Earth’s surface:
$ ghci
GHCi, version 7.4.2: http://www.haskell.org/ghc/ :? for help
Prelude> let bigG = 6.674e-11
Prelude> let mE = 6e24
Prelude> let rE = 6.4e6
Prelude> bigG * mE / rE^2
9.776367187499998
Note: I’ve removed some of ghci’s diagnostics to make things clearer.
List Comprehensions
One of Haskell’s most succinct features is the list comprehension.11 Continuing from above let’s see how g changes as we ascend from the Earth:
Prelude> let g h = bigG * mE / (rE + h)^2
Prelude> [ (h, g h) | h <- [0,10000..100000] ]
[(0.0,9.776367187499998),(10000.0,9.745887495406212),...
That’s hardly the most readable of output, but Haskell has a printf clone12 which solves the problem:
Prelude> import Text.Printf
Prelude Text.Printf> hs = [0,10000..100000]
Prelude Text.Printf> putStr $ concat [ printf "%6.0f %.4f\n" h (g h) | h <- hs ]
0 9.7764
10000 9.7459
20000 9.7156
30000 9.6854
40000 9.6553
50000 9.6254
60000 9.5956
70000 9.5660
80000 9.5365
90000 9.5071
100000 9.4779
Of course you can do more than just a simple map. It’s easy to loop over more than one variable:
$ ghci
Prelude> import Data.Numbers.Primes
Prelude Data.Numbers.Primes> [ p^i | p <- take 5 primes, i <- [1..3] ]
[2,4,8,3,9,27,5,25,125,7,49,343,11,121,1331]
or define new variables:
... > [ (p,i,n) | p <- take 5 primes, i <- [1..3], let n = p^i ]
[(2,1,2),(2,2,4),(2,3,8),(3,1,3),(3,2,9),(3,3,27),(5,1,5),(5,2,25)...
or add conditions:
... > [ (p,i,n) | p <- take 5 primes, i <- [1..3], let n = p^i, n `mod` 10 == 7 ]
[(3,3,27),(7,1,7)]
Purity
Haskell is a pure13 language which means, amongst other things, that random bits of the program can’t simply do I/O.14
However, if you just want to write the results of a calculation to a file, it’s usually possible to ignore these issues, by replacing putStr with writeFile. One twist you’ll probably need is to convert the result of the calculation into a string first. Happily show does a passable job of that most of the time (and ghci uses it implicitly):
$ ghci
Prelude> import Data.Numbers.Primes
Prelude Data.Numbers.Primes> let ps = takeWhile (< 50) primes
Prelude Data.Numbers.Primes> ps
[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47]
Prelude Data.Numbers.Primes> putStrLn $ show ps
[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47]
Prelude Data.Numbers.Primes> writeFile "primes.txt" $ show ps
Prelude Data.Numbers.Primes> ^D
$ cat primes.txt
[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47]
Source files
So far, all of these examples are just typed at the ghci prompt. In practice, for more complicated calculations, or those you want to keep, it’s helpful to save things in a Haskell file which you can then load into ghci.
I use this a lot for solving geocache15 puzzles. For example, there’s a cache in Paris where you have to identify the models of car16 and then do some simple arithmetic.
Here, in its entirety, is the trivial Haskell program I wrote as I solved it (with fake data):
a = 123
b = 123
c = 123
d = 123
e = 123
f = 123
g = 123
h = 123
i = 123
j = 123
k = 123
l = 123
m = 123
n = 123
o = 123
p = 123
xxxx = c + e + f + l + m + n + o - 96 - 52
yyyy = a + b + d + g + h + i + j + k + p + 3701 - 37
And here’s how I ran it:
$ ghci cars.hs
GHCi, version 7.4.2: http://www.haskell.org/ghc/ :? for help
... [1 of 1] Compiling Main ( cars.hs, interpreted )
Ok, modules loaded: Main.
*Main> xxxx
9999
*Main> yyyy
9999
There’s nothing very sophisticated or elegant about it, but equally the source code is almost exactly what I’d write on paper were I to do it by hand.
However, unlike the paper version, it would be easy to extend this if I wasn’t sure of some of the values and wanted to calculate all the possible coordinates. If I felt particularly keen I could even write them to a file, perhaps in a format that e.g. Google Earth could understand.
Weaknesses
Text
Although it’s not always true, I still find Perl a better tool for quickly munging text. Perhaps it’s familiarity, or perhaps the seemless integration of regexps into the language remove just that bit of friction.
External data
One of the consequences of Haskell’s immutable data and purity is that it can be messy to work with e.g. a dictionary stored in a file. The contents of that file might change, so deep in the bowels of a library you can’t simply open it and read the contents, without jumping through a hoop or two.
For small–medium sized data sets, simply embedding the data in a Haskell source file seems a reasonable hack. Typically I’d write some trivial program to create that source file, which could then be included like any other library.
References
- 1. http://www.haskell.org/
- 2. http://en.wikipedia.org/wiki/Lazy_evaluation
- 3. http://www.haskell.org/tutorial/numbers.html
- 4. http://en.wikipedia.org/wiki/Arbitrary-precision_arithmetic
- 5. http://www.haskell.org/ghc/docs/latest/html/users_guide/ghci.html
- 6. http://en.wikipedia.org/wiki/REPL
- 7. http://hackage.haskell.org/packages/hackage.html
- 8. http://www.cpan.org/
- 9. http://hackage.haskell.org/package/primes
- 10. http://en.wikipedia.org/wiki/Gravity_of_Earth
- 11. http://en.wikipedia.org/wiki/List_comprehension#Haskell
- 12. http://www.haskell.org/ghc/docs/latest/html/libraries/base/Text-Printf.html
- 13. http://www.haskell.org/haskellwiki/Functional_programming#Purity
- 14. http://www.haskell.org/haskellwiki/IO_inside
- 15. http://www.geocaching.com/
- 16. http://www.geocaching.com/seek/cache_details.aspx?guid=4674ee30-4069-45fd-b2a9-f8f8ab0bbfcf