[x^2 for x in lst]

14 Languages - Haskell

2015-12-29

The seventh language - Haskell

Introduction

Haskell is the functional language. The purest of the pure. The crown in the jewel. The king of programming languages. Well, yes. You get the picture.

It it with awe I start this small journey into the world of pure functional programming. I hope that I will be able to finish it without my self-confidence being too injured :)

Haskell is a statically and strongly type language with a type system that infers the type of almost anything.

Executing Haskell programs

There are many, I am told, different versions of Haskell. The one I've tried is called Glasgow Haskell Compile (GHC) and can be downloaded from here.

GHC is started by running the ghci command. It will open up a shell in which Haskell programs can be entered.

lars@lars-lilla:~$ ghci
GHCi, version 7.8.4: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Prelude> 42 + 4711
4753
Prelude> 
Listing 1: Executing programs in the GHC Haskell interpreter.

Haskell program stored in a text file (which customarily have an .hs extension) can be loaded into the interpreter using :load. Let's for example store this is a file square.hs :

module Main where
square x = x * x
It can be loaded into the Haskell system and run like this:
Prelude> :load square.hs
[1 of 1] Compiling Main             ( square.hs, interpreted )
Ok, modules loaded: Main.
*Main> square 2
4
Listing 2: Loading functions into the Haskell interperter.

A Haskell program can also be compiled to native code by a compiler, e.g. ghc and then executed directly. To compile and run a small Hello World-program you can do like this:

lars@lars-lilla:~/programmering/14languages/haskell$ ghc -o hello helloWorld.hs [1 of 1] Compiling Main             ( helloWorld.hs, helloWorld.o )
Linking hello ...
lars@lars-lilla:~/programmering/14languages/haskell$ ./hello 
Hello World!
Listing 3: Compile and run a Haskell program.

The option -o determines the name of the output executable.

Basic Syntax

Comments

Haskell uses double dashes -- for single line comment:

*Main> -- This is a singe line comment.
Listing 4: Single line comment.

For multi line comments, {- is used:

{-
  This is a multiline comment.
  -}
Listing 5: Multi line comment.

Hello World!

The starting point of a stand-alone Haskell program is main in the module Main (more on modules in a separate section further down). It should have a return type of IO.

To output a string to the console putStr or putStrLn is used. This means that a hello world program can be written like this:

main = putStrLn "Hello World!"
...
Hello World!
Listing 6: Hello world in Haskell.

Strings

Strings are enclosed in double quotes and characters in single quotes:

Prelude> "This is a string."
"This is a string."
Prelude> 'A'
'A'
Listing 7: Enclosing of strings and single characters.

A string is just a list of characters:

Prelude> ['T','h','i','s',' ','i','s',' ','a',' ','s','t','r','i','n','g']
"This is a string"
Listing 8: String as a list of characters.

Strings can be concatenated using the ++ operator:

Prelude> "This" ++ " is" ++ " a" ++ " string"
"This is a string"
Listing 9: Concatenating strings using ++.

Variables

Just like in Clojure let binds a value to a variable. To assign 42 to a variable, one can use the following:

Prelude> let meaning_of_life = 42
Prelude> meaning_of_life
42
Listing 10: Assigning a value to a variable.

Boolean operators and literals

Haskell supports the following boolean comparison operators:

  • ==
  • /=
  • <
  • >
  • <=
  • >=

And, or and not is written like this in Haskell: &&, || and not

The boolean literals in Haskell are True and False.

Tuple

Tuples are like fixed size, immutable lists. A tuple is created by enclosing a comma separated number of items in parenthesis. E.g. Prelude> (1,2,3) (1,2,3) Listing 11: Defining a tuple.

Lists

The syntax for creating a list is like one expects: [1,2,3].

Lists support the usual head and tail operations:

Prelude> let l = [1,2,3]
Prelude> head l
1
Prelude> tail l
[2,3]
Listing 12: The head and tail operations of a list.

It is also possible to assign the head and tail to separate variables using the (h:t) construct where the head of the list is assigned to h and the tail to t.

Prelude> let (h:t) = [1,2,3]
Prelude> h
1
Prelude> t
[2,3]
Listing 13: Assigning the head and tail of a list to variables.

We can use this construct to define recursive functions. E.g. a function that computes the sum of the elements of a list can look like this:

sumOfList [] = 0
sumOfList (h:t) = h + sumOfList t
Listing 14: Function that computes the sum of the elements of a list.

It can be used like this: Prelude> :load sumOfList.hs [1 of 1] Compiling Main ( sumOfList.hs, interpreted ) Ok, modules loaded: Main. *Main> sumOfList [1,2,3] 6 Listing 15: Using the sumOfList function.

The : operator can also be used to construct a list.

Prelude> 1:[1,3]
[1,1,3]
Listing 16: Using : to add an element as the head of a list.

To append to the end of a list you use the ++ operator. It appends the elements in the list on the right of the operator to the list on the left of the operator.

*Main> [1,2,3] ++ [4]
[1,2,3,4]
Listing 17: Using ++ to append elements to a list.

Ranges

Ranges can be used to construct a list. The syntax is like this [startPoint..endPoint]. E.g.

Prelude> [1..5]
[1,2,3,4,5]
Listing 18: Using a range to create a list.

As you can see, the default increment is 1, but it is possible to specify another increment. This is done by specifying the next element in the range after the starting element. I.e. one specifies the two first elements in the sequence. By comparing these two elements, Haskell figures out what the increment are intended to be. That's clever.

An example on how an increment can be set using this method:

Prelude> [1,3..11]
[1,3,5,7,9,11]
Listing 19: Specifying an increment in a range.

A range can be unbounded. This is done by omitting the endpoint:

[1..]
. The output from this would consume a lot of space, so let's move on to how one can use an unbound range. This can be done by using take to limit the unbounded range:
Prelude> take 10 [1..]
      [1,2,3,4,5,6,7,8,9,10]
Listing 20: Extracting a number of elements from an unbounded range.

Functions

To define a function in the shell, you use let just as when assigning values to a variable.

Prelude> let square x = x * x
Prelude> square 3
9
Listing 21: Defining a function in the shell.

We see that we don't have to surround the parameters with parenthesis.

The syntax is thus let function_name parameters = body

The parameters in the parameter list should be separated by spaces. E.g. to create and invoke a function taking two parameter the following could be used:

Prelude> let times x y = x * y
Prelude> times 3 4
12
Listing 22: A function taking two arguments.

When writing Haskell programs in files instead of in the shell, the syntax is a little bit different. To define a function then, you supply two things: a type definition and the actual body. The previous function can look like this if defined in a separate file:

times :: Int ->  Int -> Int
times x y = x * y
Listing 23: A function defined in a separate file.

In the example above times :: Int -> Int -> Int is the type declaration and times x y = x * y is the body.

As can be seen in the example, the parameters in the type declaration should be separated by ->. The return type of the function is the type to the right of the rightmost ->.

You can check the type of the function using :t functionName. E.g. to check the type the times function we just defined, we can use the following:

*Main> :t times
    times :: Int -> Int -> Int
Listing 24: Checking the type of a function

If-clauses

In Haskell if is a function. That means that it must return a value and implies that an if-clause must have an else part. The syntax is similar to many other languages. Let' try it:

Prelude> if (True) then "TRUE" else "FALSE"
"TRUE"
Prelude> if (False) then "TRUE" else "FALSE"
"FALSE"
Listing 25: Basic if-clause.

Recursion

As it is a functional language recursion have a really prominent place in the Haskell toolbox. We can implement a basic factorial function like this:

factorial n = if n <= 0 then 1 else n * factorial (n-1)
Listing 26: Function determining the factorial of a number. Written using basic recursion.

Recursive functions could also use another two features:

  • Pattern matching
  • Guards

Pattern matching we've encountered several times before in the series. One simply lists a number of versions of a function, each taking different input. When the function is called, Haskell tries to match the actual input to the different versions of the function and invokes the first that matches. Note that the order the versions are listed is important. Haskell starts with the top one and works towards the bottom of the list.

When pattern matching is used, the factorial program can look like this:

factorial 0 = 1
factorial n = n * factorial(n - 1)
Listing 27: Function determining the factorial of a number using pattern matching.

Guards are boolean conditions connected to a piece of code. If the boolean condition is fulfilled, the piece of code gets executed, otherwise it won't. Using guards we can change the order of the clauses compared to the factorial program that uses pattern matching:

factorial n
  | n > 0     = n * factorial(n - 1)
  | otherwise = 1
Listing 28: Function determining the factorial of a number using guards.

We see that each guard clause starts with a bar |, then the piece of code follows after a =. A catch-all clause called otherwise can be added in the end to catch those calls that haven't matched any of the explicit guards.

Modules

Namespacing in Haskell can be done via modules. You create a module by adding a module statement in the beginning of a source file. For example, if we would like to package our factorial function in a module we can do it like this:

module FactModule where
fact 1 = 1
fact n = n * fact (n - 1)
Listing 29: Packaging the factorial function into a module

A module can then be imported into another module, or into the shell, using the import keyword:

module Main where
import FactModule

main = print (fact 5)
Listing 30: Importing the FactModule module into another module.

Note that the import comes after the module declaration!

We can test drive the code above by loading the TestModule into the shell:

Prelude> :load TestModule.hs
[1 of 2] Compiling FactModule       ( FactModule.hs, interpreted )
[2 of 2] Compiling Main             ( TestModule.hs, interpreted )
Ok, modules loaded: Main, FactModule.
*Main> main
120
Listing 31: Test driving the TestModule.

Note that I've stored the FactModule in a file named FactModule.hs and the TestModule in the file TestModule. It's not necessary to follow this pattern, but it seems easier because then the shell will be able to find the files containing the modules without any tweaking.

Haskell standard library contains a lot of useful modules which can be accessed just by importing them. For example, the Data.Char module contains a function isDigit that can determine if a character represents a digit or not. That function can be used by importing the Data.Char as in the following example:

module Main where
    import Data.Char

    rev [] = []
    rev (h:t) = (rev t) ++ [h]

    stringToNumber [h] base = if (isDigit h) then (digitToInt h) * base else 0
    stringToNumber (h:t) base = if (isDigit h) then ((digitToInt h) * base) + (stringToNumber t (base * 10)) else (stringToNumber t base)

    sToN l = stringToNumber (rev l) 1
Listing 32: Importing a module from the standard library.

In the previous example, another function, stringToNumber, from the Data.Char module is also used.

I/O

Strings can be printed out to the console using putStr. Input can be read using getLine.

So how do we create a function that for example prints several messages to the console? Or read some input, process it, read some more input and print a message based on both entered values? That can be tricky to accomplish just using the Haskell stuff that we've covered so far. do notation to the rescue!

Using do you can program almost imperatively inside a function. You start a function body with do, add a number of statements separated by semi-colons, and end with a return statement. An example:

module Main where
  readName = do putStr "Enter you name: ";
                name <- getLine;
                putStr "Enter you age: ";
                age <- getLine;
                return ("Hi, " ++ name ++ " age: " ++ age)
Listing 33: Function using do to do several things sequentially.

The function can then be used like this:

*Main> :load readName.hs
[1 of 1] Compiling Main             ( readName.hs, interpreted )
Ok, modules loaded: Main.
*Main> readName
Enter you name: Lars
Enter you age: 42
"Hi, Lars age: 42"
Listing 34: Using the readName function.

Features

List comprehensions

Just like Python and Erlang, Haskell supports list comprehensions, and just like in those languages list comprehensions in Haskell consists of two basic parts: an expression and generators/filters. The generators and filters determines what data that should be passed on to the expression. The expression performs operations on that data to produce the elements that are included in the result. Just like in Python and Erlang.

In Haskell the expression is to the left, then comes a | and to the right of that the generators and filters. Like this:

[expression | generatorsAndFilters]

The variables used in the expression are defined in the generator section by using the arrow syntax: [x | x <- generator]

Let's exemplify. To compute a list of the squares of a number of integers this snippet could be used:

Prelude> [x^2 | x <- [1,2,3,4,5]]
[1,4,9,16,25]
Listing 35: Basic list comprehension.

Several variables can be generated by the generator by separating the variables by commas:

Prelude> [x*y | x <- [1,2,3,4,5], y <- [10,20]]
[10,20,20,40,30,60,40,80,50,100]
Listing 36: List comprehension using two variables.

Filters can be applied to the generator just by adding boolean conditions after the generators:

Prelude> [x*y | x <- [1,2,3,4,5], y <- [10,20], y < 15]
[10,20,30,40,50]
Listing 37: List comprehension including a filter.

Several filters can be separated by commas:

Prelude> [x*y | x <- [1,2,3,4,5], y <- [10,20], x > 2, y < 15]
[30,40,50]
Listing 38: List comprehension including two filters.

Composition

Sometimes we want to chain methods together. I.e. if we have functions a and b we can take the output from a and use it as indata to b. In code this is usually expressed like this: b(a()). Haskell includes a special syntax for this kind of function chaining: b . a.

For example, if we have a list on which tail we want to apply head (i.e. we want to find its second element), we can do like this: Prelude> (head . tail) [1,2,3] 2 Listing 39: Composing two functions together.

Of course, composition is not limited to only two functions. We can add more functions if we want:

Prelude> (head . tail . tail) [1,2,3]
3
Listing 40: Using three functions in a composition.

Anonymous functions

It is easy to create an anonymous function in Haskell and the syntax is minimalistic: (\param1 param2...) -> body)

An anonymous function that computes the square of an integer can look like this:

(\n -> n*n)
Listing 41: Anonymous function to compute the square of an integer.

To invoke the anonymous function you just pass the inparameters after the definition of the function:

Prelude> (\n -> n*n) 13
169
Listing 42: Invoking an anonymous function.

Locally scoped functions

A locally scoped function is a function that is defined within a function. The syntax is that use first use the "inner" function within the enclosing function, just as if it already has been defined, then you use the keyword where after which you define the function. An example will perhaps make it more clear. Let's say that we want to define a function that quadruples a number. Such a function can be implemented by applying a function that squares the input number twice. We therefore start implemeting a quadruple function that just calls a square function twice (using composition). The square we define in the where clause:

module Main where
    quadruple x = (square . square) x
      where square y = y * y
Listing 43: Defining a locally scoped function.

The above function can be loaded and tested in the ghc shell like this:

*Main> :load quadruple.hs
[1 of 1] Compiling Main             ( quadruple.hs, interpreted )
Ok, modules loaded: Main.
*Main> quadruple 2
16
Listing 44: Loading end executing the quadruple function.

Higher order functions - map

As expected, being a functional language, Haskell includes a plethora of higher order functions. map is one of them.

The functionmap does what it usually does, maps a function to every element in a list. To compute a list of the squares of a list of integers, the following code snippet could be used:

Prelude> map (\n -> n*n) [1,2,3,4,5,6,7,8,9]
[1,4,9,16,25,36,49,64,81]
Listing 45: Using map to compute the squares of the elements in a list.

Higher order functions - filter

filter is another of the standard methods in functional languages. It goes through an input list, applied a predicate to each item and returns a new list containing the elements for which the predicate returned true. In Haskell, a usage of filter can look like this:

Prelude> filter (\x -> (mod x 2) == 0) [1,2,3,4,5,6,7,8,9]
[2,4,6,8]
Listing 46: Using filter to extract all even numbers from a list.

Higher order functions - foldr, foldl, foldr1, foldl1

To perform some kind of aggregation operation on a list, the foldl and foldr methods can be used. They both take a function and a start value as parameters. The function should itself take two params the first will be set to the element of the list, the second will be the accumulated value. The difference between foldl and foldr is that foldl starts the processing of the list from the left while foldr starts from the right.

As an example, let's sum the elements in a list of integers using first foldl and then foldr:

Prelude> foldl (\elementValue currentSum -> currentSum + elementValue) 0 [1,2,3,4,5,6,7,8,9]
45
Prelude> foldr (\elementValue currentSum -> currentSum + elementValue) 0 [1,2,3,4,5,6,7,8,9]
45
Listing 47: Using foldl and foldr to sum the integers in a list.

There also exist versions of foldl and foldr that applies a binary operator instead of a function to the elements, nicely paired together, in the list. These versions are called foldl1 and foldr1. Let's rewrite the examples above to use them instead of the regular versions:

Prelude> foldl1 (+) [1,2,3,4,5,6,7,8,9]
45
Prelude> foldr1 (+) [1,2,3,4,5,6,7,8,9]
45
Listing 48: Using foldl1 and foldr1 to sum the integers in a list.

Partially applied functions

In Haskell you can create a function that calls another function, but only passes some of the parameters that the called function needs. The first function is called a partially applied function. I suppose that it is called that because it only applies some of the parameters to the function it calls. The partially applied function can then itself be called with the remaining parameters, so that in the end, all parameters needed by the second function have values and the function can be executed. Let's exemplify.

Suppose that we a method that can compute x^y called raisedTo . Let's say that we wanted to create a function that can square numbers. That could be done by creating a partially applied function taking one param that calls raisedTo supplying 2 for the y value:

Prelude> let raisedTo x y = x ^ y
Prelude> let square x = raisedTo x 2
Prelude> square 5
25
Listing 49: Creating a partially applied function to square a number.

In fact, the process of creating partially applied function is how Haskell handles all functions having more than one parameter. For each parameter more than the first, Haskell creates a partially applied function that it lets the surrounding method call with the parameter in question. This way, all the "extra" parameters are handled one by one until we end up with an outer function that only takes one parameter. When calling the outermost function with all parameters, it will call the first wrapped function with the last param. That function will then call the function it wraps with its param and so on. This process of calling partially applied functions is called currying.

That Haskell uses partially applied functions to handle functions taking more than one parameter can be seen if one checks the type of such a function using :t.

Prelude> let times x y = x * y
Prelude> :t times
times :: Num a => a -> a -> a
  
Listing 50: Checking the type of a function taking two parameters.

We see that instead of saying that times is a function taking two numbers as params and returns a number, it says that times takes one parmeter, applies one function to it and that the result of that function call is the result of the original call.

To mimic this way of handling functions taking more than one param, we can rewrite an invocation of times 3 4 using an anonymous function taking one param which is multiplied by 3. This anonymous function is then invoked with the param 4. Let's see how to do this:

Prelude> (\x -> x * 3) 4
12
Listing 51: Using an anonymous function to compute 3 * 4.

Lazy evaluation and infinite lists

In Haskell, as in Clojure, you can create infinite lists and, also just like in Clojure, you work on then using lazy evaluation techniques. We've already seen the take function in the section on ranges, but there are also other functions like drop and head that you can use to extract parts of an infinite sequence.

To create an infinite sequence, you can either use the range function or you can write a function that recurses infinitely, e.g.

module Main where
      infiniteSequence start = start : (infiniteSequence (start + 1))
Listing 52: Function creating an infinite sequence.

The function above creates an infinite sequence of numbers starting with a start number and where the next number always is one greater that the previous one. We can use the function like this:

Prelude> :load infiniteSequence.hs
[1 of 1] Compiling Main             ( infiniteSequence.hs, interpreted )
Ok, modules loaded: Main.
*Main> take 7 (infiniteSequence 10)
[10,11,12,13,14,15,16]
Listing 53: Using the function that creates an infinite sequence.

Using infinite sequences we can quickly build a cool function that generates the Fibonacci sequence. To to that we need a function that given two consecutive numbers in the sequence, generates all following numbers. Such a method can look like this: fibonacciNumbersFrom x y = x : (fibonacciNumbersFrom y (x + y)).

We then create a function that generates the full sequence by supplying the first two numbers in the series to fibonacciNumbersFrom. Like this: fibonacciSequence = fibonacciNumbersFrom 1 1

Finally we create a function that returns the n:th number in the series by taking the n first numbers, dropping the first n - 1 of those and returning the single remaining number: fibonacci x = head (drop (x - 1) (take x fibonacciSequence))

The whose program look like this:

module Main where
  fibonacciNumbersFrom x y = x : (fibonacciNumbersFrom y (x + y))
  fibonacciSequence = fibonacciNumbersFrom 1 1
  fibonacci x = head (drop (x - 1) (take x fibonacciSequence))
Listing 54: Cool Fibonacci program.

Let's try it:

*Main> fibonacci 1
1
*Main> fibonacci 2
1
*Main> fibonacci 3
2
*Main> fibonacci 1000
43466557686937456435688527675040625802564660517371780402481729089536555417949051890403879840079255169295922593080322634775209689623239873322471161642996440906533187938298969649928516003704476137795166849228875
Listing 55: Test driving the cool Fibonacci program.

Seems to work fine ;)

Gotchas

  • The import statements in a module must appear after the module declaration, not before like in Java.

FizzBuzz in Haskell

module Main where

checkModThreeAndFive :: Int -> (IO (), Int)
checkModThreeAndFive n = if ((mod n 3 == 0) && (mod n 5 == 0)) then (putStrLn "fizzBuzz" >> io, retValOne) else (io2, retValTwo)
    where (io, retValOne) = loop (n+1)
          (io2, retValTwo) = checkModThree n

checkModThree :: Int -> (IO (), Int)
checkModThree n = if (mod n 3 == 0) then (putStrLn "fizz" >> io, retValOne) else (io2, retValTwo) where
    (io, retValOne) = loop (n+1)
    (io2, retValTwo) = checkModFive n

checkModFive :: Int -> (IO (), Int)
checkModFive n = if (mod n 5 == 0) then (putStrLn "buzz" >> io, retVal) else (print n >> io, retVal) where
    (io, retVal) = loop (n+1)

loop :: Int -> (IO (), Int)
loop n = if n <= 100 then (io, retVal) else (return (), 0) where
    (io, retVal) = checkModThreeAndFive n

 main = fst $ loop 1
                 

What I like about Haskell

  • Infinite sequences are cool.
  • Guards in functions. Sometimes problems can be expressed really nicely using guards in a function.

What I don't like about Haskell

  • The error messages from the compiler is often very hard to understand. At least for a beginner.
  • The learning curve. It is quite steep.
  • Some things that are really easy to accomplish in most other languages, like writing a FizzBuzz program, are really hard (for a beginner at least) to do in Haskell.

Onwards, upwards

This was the last language in the first part of the series, i.e. Haskell was the last language in the book Seven Languages in Seven Weeks. Now I will take a short break before continuing with the eight language. Among other things, I'll write a review of Seven Languages in Seven Weeks. I plan on starting with the next language in february or so. See you next year!

Previous parts in the series

1. Ruby
2. Io
3. Prolog
4. Scala
5. Erlang
6. Clojure

Links

[1] Haskell homepage


Leave a reply

Your name as it will be displayed when the comment is posted on the page. Your email address will not be published.