[Published in Open Source For You (OSFY) magazine, August 2014 edition.]
This second article in the series on Haskell explores a few functions.
Consider the function sumInt to compute the sum of two integers. It is defined as:
sumInt :: Int -> Int -> Int sumInt x y = x + y
The first line is the type signature where the function name, arguments and return types are separated using a double colon (::). The arguments and the return types are separated by the symbol (->). Thus, the above type signature tells us that the sum function takes two arguments of type Int and returns an Int. Note that the function names must always begin with the letters of the alphabet in lower case. The names are usually written in CamelCase style.
You can create a Sum.hs Haskell source file using your favourite text editor, and load the file on to the Glasgow Haskell Compiler interpreter (GHCi) using the following code:
$ ghci GHCi, version 7.6.3: 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> :l Sum.hs [1 of 1] Compiling Main ( Sum.hs, interpreted ) Ok, modules loaded: Main. *Main> :t sumInt sumInt :: Int -> Int -> Int *Main> sumInt 2 3 5
If we check the type of sumInt with arguments, we get:
*Main> :t sumInt 2 3 sumInt 2 3 :: Int *Main> :t sumInt 2 sumInt 2 :: Int -> Int
The value of sumInt 2 3 is an Int as defined in the type signature. We can also partially apply the function sumInt with one argument and its return type will be Int -> Int. In other words, sumInt 2 takes an integer and will return an integer with 2 added to it.
Every function in Haskell takes only one argument. So, we can think of the sumInt function as one that takes an argument and returns a function that takes another argument and computes their sum. This return function can be defined as a sumTwoInt function that adds a 2 to an Int using the sumInt function, as shown below:
sumTwoInt :: Int -> Int sumTwoInt x = sumInt 2 x
The ‘=’ sign in Haskell signifies a definition and not a variable assignment as seen in imperative programming languages. We can thus omit the ‘x’ on either side and the code becomes even more concise:
sumTwoInt :: Int -> Int sumTwoInt = sumInt 2
By loading Sum.hs again in the GHCi prompt, we get the following:
*Main> :l Sum.hs [1 of 1] Compiling Main ( Sum.hs, interpreted ) Ok, modules loaded: Main. *Main> :t sumTwoInt sumTwoInt :: Int -> Int *Main> sumTwoInt 3 5
Let us look at some examples of functions that operate on lists. Consider list ‘a’ which is defined as [1, 2, 3, 4, 5] (a list of integers) in the Sum.hs file (re-load the file in GHCi before trying the list functions).
a :: [Int] a = [1, 2, 3, 4, 5]
The head function returns the first element of a list:
*Main> head a 1 *Main> :t head head :: [a] -> a
The tail function returns everything except the first element from a list:
*Main> tail a [2,3,4,5] *Main> :t tail tail :: [a] -> [a]
The last function returns the last element of a list:
*Main> last a 5 *Main> :t last last :: [a] -> a
The init function returns everything except the last element of a list:
*Main> init a [1,2,3,4] *Main> :t init init :: [a] -> [a]
The length function returns the length of a list:
*Main> length a 5 *Main> :t length length :: [a] -> Int
The take function picks the first ‘n’ elements from a list:
*Main> take 3 a [1,2,3] *Main> :t take take :: Int -> [a] -> [a]
The drop function drops ‘n’ elements from the beginning of a list, and returns the rest:
*Main> drop 3 a [4,5] *Main> :t drop drop :: Int -> [a] -> [a]
The zip function takes two lists and creates a new list of tuples with the respective pairs from each list. For example:
*Main> let b = ["one", "two", "three", "four", "five"] *Main> zip a b [(1,"one"),(2,"two"),(3,"three"),(4,"four"),(5,"five")] *Main> :t zip zip :: [a] -> [b] -> [(a, b)]
The let expression defines the value of ‘b’ in the GHCi prompt. You can also define it in a way that’s similar to the definition of the list ‘a’ in the source file.
The lines function takes input text and splits it at newlines:
*Main> let sentence = "First\nSecond\nThird\nFourth\nFifth" *Main> lines sentence ["First","Second","Third","Fourth","Fifth"] *Main> :t lines lines :: String -> [String]
The words function takes input text and splits it on white space:
*Main> words "hello world" ["hello","world"] *Main> :t words words :: String -> [String]
The map function takes a function and a list and applies the function to every element in the list:
*Main> map sumTwoInt a [3,4,5,6,7] *Main> :t map map :: (a -> b) -> [a] -> [b]
The first argument to map is a function which is enclosed within parenthesis in the type signature (a -> b). This function takes an input of type ‘a’ and returns an element of type ‘b’. Thus, when operating over a list [a], it returns a list of type [b].
Recursion provides a means of looping in functional programming languages. The factorial of a number, for example, can be computed in Haskell, using the following code:
factorial :: Int -> Int factorial 0 = 1 factorial n = n * factorial (n-1)
The definition of factorial with different input use cases is called as pattern matching on the function. On running the above example with GHCi, you get:
*Main> factorial 0 1 *Main> factorial 1 1 *Main> factorial 2 2 *Main> factorial 3 6 *Main> factorial 4 24 *Main> factorial 5 120
Functions operating on lists can also be called recursively. To compute the sum of a list of integers, you can write the sumList function as:
sumList :: [Int] -> Int sumList  = 0 sumList (x:xs) = x + sumList xs
The notation *(x:xs) represents a list, where ‘x’ is the first element in the list, and ‘xs’ is the rest of the list. On running sumList with GHCi, you get the following:
*Main> sumList  0 *Main> sumList [1,2,3] 6
Sometimes, you will need a temporary function for a computation, which you will not need to use elsewhere. You can then write an anonymous function. A function to increment an input value can be defined as:
*Main> (\x -> x + 1) 3 4
Such functions are called as Lambda functions, and the ‘\’ represents the notation for the symbol Lambda. Another example is given below:
*Main> map (\x -> x * x) [1, 2, 3, 4, 5] [1,4,9,16,25]
It is a good practice to write the type signature of the function first when composing programs, and then write the body of the function. Haskell is a functional programming language, and understanding the use of functions is very important.