Haskell basics

Defining functions

A function which doubles a number would be written as follows:

double x = x * 2

The first part is the function name, double, followed by a list of parameters which in this case is just x then equals and the function definition.

The definition above is just syntactic sugar for a lambda function. The equivalent lambda function is double = \x -> x * 2.

Operators

Unlike functions, operators go between their arguments. Operators are infix, as opposed to functions which are prefix. To use an function like an operator it should be surrounded with backticks. To use an operator like a function it should be surrounded with parentheses.

Associativity

Every operator in Haskell is either left or right associative. This means in the absence of brackets the associativity of an operator determine where the brackets will go. Consider the + operator and the expression a + b + c. If + is left associative, this will become (a + b) + c whereas if it is right associative it will become a + (b + c). In reality, + is left associative.

Function application in Haskell is left associative. double double 5 is the same as (double double) 5.

Operators also have precedence from 0 to 9 which determines the order of operations. A higher precedence means a more important operator. Functions have precedence 10 which means they are always applied before operators.

Fixity declarations are used to declare the associativity and precedence of an operator. The fixity declaration for addition is infixl 6 +. This means it has left associativity (from the l after infix) and precedence 6.

Pattern matching

There are three main types of pattern matching - case ... of , guards and top level pattern matching.

Case

The case ... of statement works like case statements in other languages. The recursive factorial function could be written as:

fact x = case x of
    0 -> 1
    n -> n * f (n-1)

Guards

Guards use | to match predicates. The factorial function above could be written using guards as follows:

fact x
    | x == 0    = 1
    | otherwise = x * f (x-1)

Top level pattern matching

Top level pattern matching matches the arguments directly. For the factorial example this would be:

fact 0 = 1
fact x = x * f (x-1)

Let and where

Let and where can be used to define additional expressions in a function definition. For example the definition of a function which calculates the area of a circle from its diameter can be written as follows:

circleArea d = pi * (d/2) * (d/2)

This can be simplified using let:

circleArea d = let r = d/2 in pi * r * r

or using where:

circleArea d = pi * r * r where r = d/2

Recursion

See recursion.

Recursion is where a function calls itself. Recursive functions must have a base case or they will never terminate. Recursion can be either explicit or implicit. Explicit recursion is where a function calls itself in its definition whereas implicit recursion uses a predefined recursive function but there is no recursion in the function itself. For example, the definition of sum would be explicit recursion but a function which uses sum would be implicit recursion.