ruk·si

Haskell Guide

Updated at 2014-10-28 10:40

Haskell is the most commonly used pure functional language.

Keep in mind that Haskell is a very different kind of programming language. All terms like instance, class and return have different meaning in Haskell context.

All examples in this note are written so they can be ran as separate programs using a Haskell compiler e.g. compile online - haskell.

Glasgow Haskell Compiler (GHC) is the leading implementation of Haskell. You can download it from www.haskell.org/platform.

Coding Style

Maximum line length is 80 characters.

Indention is made using spaces. Prefer 4 space indention but focus in showing the logical structure of the code. Tabs are illegal.

Use one blank line between top-level definitions. No more, no less.

Type and data constructors are in pascal case. Everything else in camel case.

Int
Maybe
functionNamesAreLowercase
connectDatabase
argumentsAreLowercase
x
bird
listsHaveSuffixs
xs
birds

Underscore _ as a name means that the value assigned to it won't be used.

Names ending with a apostrophe ' means an alternative version. This usually means the function or variable is lazily evaluated, but not always. Good to know but you should never name anything like this.

doubleSmall x = if x < 10
                then x * 2
                else x
doubleSmall' x = (if x < 10 then x * 2 else x) + 1

main = do
    print (doubleSmall 9)  -- 18
    print (doubleSmall' 9) -- 19

General

Code consists of Haskell scripts ending in .hs suffix.

-- test.hs
double x = x + x
# ghci test.hs
> double 2
4
# use :reload command in ghci after changing the file.
> :reload

Entry point of a Haskell program is the main function.

main = print "Hello world!" -- "Hello world!"

Definitions are made with =. If you specify parameters, it's a function definition. If you don't specify any parameters, it's a name definition.

doubleMe x = x * 2
personAge = 6
main = print (doubleMe personAge) -- 12

Function applications is usually by prefix or inflix syntax. All prefix functions can be used as a inflix function by adding backticks.

main = do
    print (div 92 10)    -- 9 (prefix call)
    print (1 + 1)        -- 2 (inflix call)
    print (92 `div` 10)  -- 9 (prefix as inflix call)

$ makes the previous function application right-associative. Basically, it takes whatever is on the right side and applies it to the left side. It's used to clean up the code or to apply a series of functions on a single value.

main = do
    print (map (+1) [1,2,3]) -- [2,3,4]
    print $ map (+1) [1,2,3] -- [2,3,4]
    print (map ($ 3) [(+ 4), (* 10), (^ 2)]) -- [7,30,9]

. combines functions. It means function composition. The functions are applied from right to left. It's used to clean up the code.

main = do
    print (map (negate . abs) [1,0,-3,11,-20]) -- [-1,0,-3,-11,-20]

\ creates a lambda. Lambdas are anonymous functions that are defined and used only in one place.

main = do
    print (map (\(a,b) -> a + b) [(1,2), (3,4), (1,1)]) -- [3,7,2]

Functions are curried. Function that seems to take several parameters actually takes just one parameter and returns a function that takes the next parameter and so on. Every -> identifies a single function that takes what is on the left side and returns what is on the right side.

-- Function with this type:
a -> b -> c
-- In fact just returns a function with this type:
b -> c
-- Which returns:
c
-- `max` definition.
-- max :: (Ord a) => a -> a -> a

-- These two following lines are equivalent because of currying.
main = do
    print (max 4 5)   -- 5
    print ((max 4) 5) -- 5

Function as parameters are defined by (a -> a).

applyTwice :: (a -> a) -> a -> a
applyTwice f x = f (f x)
main = print (applyTwice (+3) 10) -- 16

Architecture

You include other Haskell modules with the import keyword.

-- Import `nub` function from `Data.List` module.
-- You can import everything from the module with `import Data.List`
-- You can import except something with `import Data.List hiding (sort)`
-- You can name imports with `import qualified Data.List as L`, and
-- then use them with `L.nub`.
import Data.List (nub)

-- Returns number of unique elements in a list.
uniqueCount :: (Eq a) => [a] -> Int
uniqueCount = length . nub

main = do
    print (uniqueCount ([] :: [Int]))   -- 0
    print (uniqueCount [1])             -- 1
    print (uniqueCount [1,2,1,2,4,3,3]) -- 4

You create own modules with the module keyword.

-- You could import this module with `import Geometry` if
-- `Geometry.hs` is in the same folder.
module Geometry
( sphereVolume
, sphereArea
) where

sphereVolume :: Float -> Float
sphereVolume radius = (4.0 / 3.0) * pi * (radius ^ 3)

sphereArea :: Float -> Float
sphereArea radius = 4 * pi * (radius ^ 2)

You should use submodules to keep your main modules clean.

-- Directory `Geometry` with `Sphere.hs`.
module Geometry.Sphere
( volume
, area
) where

volume :: Float -> Float
volume radius = (4.0 / 3.0) * pi * (radius ^ 3)

area :: Float -> Float
area radius = 4 * pi * (radius ^ 2)

Monads

Monads are one of the fundamental concepts of Haskell. They should be thought as computation builders which chain operations together. Understanding monads is as important as understanding conditional statements in other programming languages.

-- Input/output actions work using the IO monad.
main = print "Hello world!" -- "Hello world!"
-- Computations that might return nothing work using the Maybe monad.
-- Value is Just x or Nothing.
main = print (Just "Hello!")           -- Just "Hello!", a Maybe String
--  List comprehension works using the List monad.
main = print [x*2 | x <- [1..10], odd x] -- [2,6,10,14,18]

Monad is basically something that wraps a value or values (e.g. Int, String) while implementing instructions how to wrap a value, unwrap the value and apply the wrapped value to a function. return, >>=, >> and fail are the functions that must be implemented but most of the time the default implementations >> and fail work nicely. >>= is called bind.

-- Example definition of the Maybe monad.
data Maybe a = Nothing | Just a

instance Monad Maybe where
    Nothing  >>= f = Nothing -- calling a function while wrapping Nothing
    (Just x) >>= f = f x     -- calling a function while wrapping Just value
    return         = Just    -- minimal warpper for a value

Both list comprehension and do-notation are just syntactical sugar for monad operators.

-- Do-notation.
main = do
    print (succ 8)    -- 9
    print (min 19 20) -- 19
    print (max 19 20) -- 20
-- Do-notation behind the scenes.
main =
    print (succ 8)    >> -- 9
    print (min 19 20) >> -- 19
    print (max 19 20)    -- 20
--  List comprehension.
main = print [x*2 | x <- [1..10], odd x] -- [2,6,10,14,18]
-- List comprehension behind the scenes.
main = print ([1..10] >>= (\x -> if odd x then [x*2] else []))
-- [2,6,10,14,18]

Types

Every expression has a type. You can get the type of an expression with :t in GHCi.

> :t True
True :: Bool
> :t "Hello"
"Hello" :: [Char]
> :t not
not :: Bool -> Bool
-- basic types
Int     -- Bound so has max and min. Range depends on the computer word size.
        -- You can get the range with `minBound :: Int` and `maxBound :: Int`.
Integer -- Not bound so can store big numbers.
Float   -- Real floating-point number with single precision.
Double  -- Real floating-point number with double precision.
Bool    -- Boolean, True or False.
Char    -- An unicode character, denoted by single quotes ''.
String  -- A list of chars so the same as [Char], denoted by double quotes "".

-- a little bit more complex types
Num         -- Int, Integer, Float and Double are all Num (numbers).
Floating    -- Float and Double are floating-point numbers.
Integral    -- Int and Integer are integral numbers.
Ord         -- Type has a comparable order.

:: operator makes an explicit type declaration. :: is read "has type of".

-- removeNonUppercase "has type of" function that takes
-- a string and returns a string.
removeNonUppercase :: [Char] -> [Char]
removeNonUppercase st = [c | c <- st, c `elem` ['A'..'Z']]

main = do
    print (removeNonUppercase "Hello There!")   -- "HT"
    print (10 :: Double)                        -- 10.0 "has type of" Double

show returns the string representation. The counter operation read turns a string into another type, but sometimes you must define the type if compiler cannot figure it out from the context.

main = do
    print (1)                   -- 1
    print (show 1)              -- "1"
    print (read "1" + 10)       -- 11
    print ((read "1") :: Int)   -- 1

data defines a new data type.

-- Noobean can be `No` or `Yes`.
data Noobean = No | Yes

data Point = Point Float Float

-- We have two kinds of shapes, circles and rectangles.
-- Circle and Rectangle are value constructors => they are functions, not types.
data Shape = Circle Point Float | Rectangle Point Point

area :: Shape -> Float
area (Cirle _ r) = pi * r ^ 2
area (Rectangle (Point x1 y1) (Point x2 y2)) = (abs $ x2 - x1) * (abs $ y2 - y1)

deriving allows specifying the other types that the data type implements.

data Day = Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday
    deriving (Eq, Ord, Show, Read, Bound, Enum)
    -- Eq = values have a concept of being equal `==`
    -- Ord = values has order, Monday is the lowest, Sunday is the highest
    -- Show = can be turned to string
    -- Read = can be turned from to string to this type
    -- Bound = has maximum and minimum value
    -- Enum = values have predecessor and/or successor.

Data types also have an alternative record syntax.

data Person = Person
    { firstName :: String
    , lastName :: String
    , age :: Int
    } deriving (Show)

tommi = Person {firstName = "Tommi", lastName = "Laine", age = 26}

main = do
    print tommi -- Person {firstName = "Tommi", lastName = "Laine", age = 26}
    print (lastName tommi) -- "Laine"

Type constructors are predefined templates to create new data types.

-- `Maybe` is a type constructor that creates a new type when used.
-- data Maybe a = Nothing | Just a
-- E.g. Maybe String creates a new type which may be a string or nothing.

main = do
    print (Just "Hello!" :: Maybe String) -- Just "Hello!", explicit
    print (Just "Hello!")                 -- Just "Hello!", implicit
    print (Nothing :: Maybe String)       -- Nothing

type creates a type alias. It doesn't create a new type, it only creates a reference to an existing type.

type PenguinCount = Int
main = print (10 :: PenguinCount) -- 10

class allows defining data type behaviour; a type class.

-- Here is how `Eq` is implemented in the standard library.
-- class Eq a where
--     (==) :: a -> a -> Bool
--     (/=) :: a -> a -> Bool
--     x == y = not (x /= y)
--     x /= y = not (x == y)

instance allows implementing shared data type behaviour.

data TrafficLight = Red | Yellow | Green

instance Eq TrafficLight where
    Red == Red = True
    Yellow == Yellow = True
    Green == Green = True
    _ == _ = False

instance Show TrafficLight where
    show Red = "Red light"
    show Yellow = "Yellow light"
    show Green = "Green light"

main = print (Yellow :: TrafficLight) -- Yellow light

Lists

Lists can only have elements of one type.

myNumbers = [4,8,15,16,32,42]
main = print myNumbers -- [4,8,15,16,32,42]

!! is list access. This should rarely be needed though.

myNumbers = [4,8,15,16,32,42]
main = print (myNumbers !! 4 ) -- 32

++ is concatenation. Notice that ++ must go through the first elemnt fully before concetenating.

smallNumbers = [2,4,7]
bigNumbers = [999,1234,7890]
firstName = "Tommi"
lastName = "Laine"
main = do
    print (smallNumbers ++ bigNumbers)   -- [2,4,7,999,1234,7890]
    print (firstName ++ " " ++ lastName) -- "Tommi Laine"

: is prepend. Much more performant than concatenation, but requires a single list element that is added to the list.

lastName = "Laine"
main = print ('T':'o':'m':'m':'i':' ':lastName) -- "Tommi Laine"

Strings are only lists of characters. You can use list functions on strings.

main = print ("Ruksi" == ['R','u','k','s','i']) -- True

Comparison of lists is done by individual elements. It first takes the first elements of both lists and compares them until the lower or greater is found.

main = do
    print ([3,1,1] > [2,4,0])   -- True, because 3 > 2
    print ([3,2,1] > [2,3,0,0]) -- True, because 3 > 2
    print ([3] > [2,1,0,0])     -- True, because 3 > 2
    print ([3,3,0] > [3,2,1])   -- True, because 3 == 3, but 3 > 2

Basic list functions.

main = do
    print (head [3,2,1])        -- 3
    print (tail [3,2,1])        -- [2,1]
    print (last [3,2,1])        -- 1
    print (init [3,2,1])        -- [3,2]
    print (reverse [3,2,1])     -- [1,2,3]
    print (take 2 [3,2,1])      -- [3,2]
    print (take 4 [3,2,1])      -- [3,2,1]
    print (drop 2 [3,2,1])      -- [1]
    print (drop 4 [3,2,1])      -- []
    print (maximum [3,2,1])     -- 3
    print (minimum [3,2,1])     -- 1
    print (sum [3,2,2])         -- 7
    print (product [3,2,2])     -- 12
    -- NOTE: Doing any of the previous on an empty lists will crash.

    -- Checking if list contains values.
    print (length [3,2,1])      -- 3
    print (null [3,2,1])        -- False
    print (null [])             -- True

    -- Checking if list contains a specific element.
    print (8 `elem` [3,2,1])    -- False
    print (8 `elem` [3,8,1])    -- True

Ranges

You can generate lists using ranges.

main = do
    print [1..10]    -- [1,2,3,4,5,6,7,8,9,10]
    -- You can specify first two elements and Haskell figures out the step.
    print [1,2..10]  -- [1,2,3,4,5,6,7,8,9,10]
    print [10,9..1]  -- [10,9,8,7,6,5,4,3,2,1]
    print [2,4..10]  -- [2,4,6,8,10]
    -- Ranges work with characters.
    print ['a'..'z'] -- "abcdefghijklmnopqrstuvwxyz"
    print ['K'..'O'] -- "KLMNO"

Ranges can be infinite but then you can utilize only part of it.

main = print (take 10 [3,5..]) -- [3,5,7,9,11,13,15,17,19,21]

Avoid using floats with ranges. Or prepare to fix a lot of bugs.

main = print [0.3,0.6..0.9] -- [0.3,0.6,0.8999999999999999]

Additional functions to generate lists.

main = do
    print (take 10 (cycle [1,2,3])) -- [1,2,3,1,2,3,1,2,3,1]
    print (take 11 (cycle "WAT "))  -- "WAT WAT WAT"
    print (take 3 (repeat 10))      -- [10,10,10]
    print (replicate 3 10)          -- [10,10,10]

List Comprehension

List comprehension allows reading and manipulating a list using a function.

main = do
    -- We draw parameter from the source by binding them to the x.
    --    [output_element | input_element <- source, optional filters]
    print [x * 2 | x <- [1..10]] -- [2,4,6,8,10,12,14,16,18,20]

You can add predicates in list comprehension to filter lists.

main = do
    -- All numbers between 1 and 10 that leave 2 behind after dividing with 3.
    print [x | x <- [1..10], x `mod` 3 == 2] -- [2,5,8]
tickTock xs = [if x < 5 then "tick" else "tock" | x <- xs, odd x]
main = do
    print [1..10]                   -- [1,2,3,4,5,6,7,8,9,10]
    print [x | x <- [1..10], odd x] -- [1,3,5,7,9]
    print (tickTock [1..10])        -- ["tick","tick","tock","tock","tock"]
main = do
    -- You can specify multiple predicates.
    print [x | x <- [1..10], x /= 3, x /= 5, x /= 9] -- [1,2,4,6,7,8,10]
removeNonUppercase st = [c | c <- st, c `elem` ['A'..'Z']]
main = do
    -- Strings are lists too.
    print (removeNonUppercase "What is this Madness.") -- "WM"
xss = [[1..4],[1..6],[1..10]]
main = do
    -- Nested lists.
    print [[x | x <- xs, even x] | xs <- xss] -- [[2,4],[2,4,6],[2,4,6,8,10]]

Single list comprehension may draw parameters from multiple lists. Note that elements are combined in all the possible ways.

listA = [1..3]
listB = [40,50,60]
main = do
    print listA                             -- [1,2,3]
    print listB                             -- [40,50,60]
    print [x + y | x <- listA, y <- listB ] -- [41,51,61,42,52,62,43,53,63]
    print [x + y | x <- listA, y <- listB, x + y > 60] -- [61,62,63]
myLength xs = sum [1 | _ <- xs]
main = do
    print (myLength [1..3])    -- 3
    print (myLength [10..100]) -- 91

Tuples

Tuples are used to store several values as a single value.

main = do
    print (1,2,3)        -- (1,2,3)
    print (6,'a',"Cain") -- (6,'a',"Cain")

Tuple's size and contained types define the tuple's type.

main = do
    print [(1,2), (3,4)]         -- [(1,2),(3,4)]
    print [(1,2), (3,4)]         -- [(1,2),(3,4)]
    print [('a','b'), ('c','d')] -- [('a','b'),('c','d')]
    -- print [(1,2), (3,4,5)]    -- This would crash, size mismatch.
    -- print [(1,2), (3,'b')]    -- This would crash, element type mismatch.

    print ((1,2) == (1,2)) -- True
    print ((1,2) == (2,1)) -- False
    -- print ((1,2) == (1,2,4)) -- This would crash, size mismatch.

Pair is the most common tuple type. fst (first) and snd (second) functions only work on pairs.

main = do
    print (fst ('a',9)) -- 'a'
    print (snd ('a',8)) -- 8

zip creates pairs.

main = do
    print (zip [1,2] [5,5])               -- [(1,5),(2,5)]
    print (zip [1,2,3,4] [5,5,5,5])       -- [(1,5),(2,5),(3,5),(4,5)]
    print (zip [1,2,3,4] (repeat 5))      -- [(1,5),(2,5),(3,5),(4,5)]
    print (zip [1..] ["orange", "apple"]) -- [(1,"orange"),(2,"apple")]
    -- Notice that extra elements are ignored on both sides.

You can generate all kinds of tuples with list comprehension.

-- All possible combinations of (a,b,c), where a, b and c are between 1 - 5.
triples = [(a,b,c) | a <- [1..5], b <- [1..5], c <- [1..5]]
main = print triples

Pattern Matching

Use pattern matching. Pattern matching allows conditional execution depending on the parameters.

sayMe :: Int -> String
sayMe 1 = "One!"
sayMe 2 = "Two!"
sayMe 3 = "Three!"
sayMe _ = "Not between 1 and 5!"

main = do
    print (sayMe 1) -- "One!"
    print (sayMe 2) -- "Two!"
    print (sayMe 3) -- "Three!"
    print (sayMe 4) -- "Not between 1 and 5!"
factorial :: Integer -> Integer
factorial 0 = 1
factorial n = n * factorial (n - 1)

main = print (factorial 20) -- 2432902008176640000
--- Pattern matching works with tuples too.
addVectors :: (Int, Int) -> (Int, Int) -> (Int, Int)
addVectors (x1, y1) (x2, y2) = (x1 + x2, y1 + y2)

second :: (Int, Int, Int) -> Int
second (_, y, _) = y

main = do
    print (addVectors (1,1) (1,2))  -- (2,3)
    print (addVectors (1,5) (-1,2)) -- (0,7)
    print (second (9,0,7))          -- 0

Include a "catch all" pattern as the final pattern. It might be raising an error but define it so next reader can understand that the error is intentional.

-- This is usually bad.
getCode :: Char -> String
getCode 'a' = "Alpha"
getCode 'b' = "Beta"
getCode 'c' = "Charlie"
-- should have getCode x = error "not defined for x" or something

main = do
    print (getCode 'a') -- "Alpha"
    print (getCode 'd') -- Non-exhaustive patterns in function getCode...
    print (getCode 'b') -- Never gets called as crashes on the previous line.

Handle empty values in patterns. Operating on an emtpy lists causes frequent problems. At least use Error monad to indicate that what you are doing is intentional.

firstLetter :: String -> String
firstLetter "" = error "firstLetter: empty string"
firstLetter all@(x:xs) = "The first letter of "++ all ++ " is " ++ [x]

main = do
    print (firstLetter "Ruksi")     -- "The first letter of Ruksi is R"
    print (firstLetter "")          -- Crashes here.
    print (firstLetter "Batman")    -- Not called as crashed.
tell :: (Show a) => [a] -> String
tell []       = "An empty list."
tell (x:[])   = "List with one element: " ++ show x
tell (x:y:[]) = "List with two elements: " ++ show x ++ " and " ++ show y
tell (x:y:_)  = "List has more than two elements."

main = do
    print (tell ([] :: [Int])) -- We must define the array type in this example.
    print (tell [1])
    print (tell [1,2])
    print (tell [1,2,3])

Guards | are used to extend pattern matching allowing conditional checks.

bmiTell :: Double -> String
bmiTell bmi
    | bmi <= 18.5 = "You're underweight."
    | bmi <= 25.5 = "You're normal."
    | bmi <= 30.5 = "You're fat."
    | otherwise   = "You're lying."

max' :: (Ord a) => a -> a -> a
max' a b
    | a <= b    = b
    | otherwise = a

main = do
    print (bmiTell 10.3)    -- "You're underweight."
    print (bmiTell 24.9)    -- "You're fat."
    print (bmiTell 42.42)   -- "You're lying."
    print (max' 10 1)       -- 10
    print (max' 9 100)      -- 100

Prefer pattern matching over case or guards.

-- Case variant.
tellInt :: Int -> String
tellInt i = case i of
    0 -> "Zero!"
    1 -> "One!"
    2 -> "Two!"
    _ -> "Other!"

-- Pattern matching variant.
tellInt' :: Int -> String
tellInt' 0 = "Zero!"
tellInt' 1 = "One!"
tellInt' 2 = "Two!"
tellInt' _ = "Other!"

-- Guard variant.
tellInt'' :: Int -> String
tellInt'' num
  | num == 0  = "Zero!"
  | num == 1  = "One!"
  | num == 2  = "Two!"
  | otherwise = "Other!"

main = do
    print (tellInt 0) -- "Zero!"
    print (tellInt 3) -- "Other!"
    print (tellInt' 0) -- "Zero!"
    print (tellInt' 3) -- "Other!"
    print (tellInt'' 0) -- "Zero!"
    print (tellInt'' 3) -- "Other!"

where

where defines temporary variables in guards and functions. Variables assinged in where are only visible to the previous function. For example, variables defined in a where are only visible to the pattern the where follows.

myFunc :: Float -> Float -> Float -> String
myFunc x y z
    | half > reallyBigHalf = "Total is really big!"
    | total > 10           = "Total is more than 10."
    | total < 10           = "Total is less than 10."
    where total = x + y + z
          half = total / 2
          reallyBigHalf = 100

main = do
    print (myFunc 1.0 2.5 3.6)      -- "Total is less than 10."
    print (myFunc 4.1 5.2 3.3)      -- "Total is more than 10."
    print (myFunc 300.0 3.0 1.0)    -- "Total is really big!"

You can use pattern matching in where.

initials :: String -> String -> String
initials firstName lastName = [f] ++ ". " ++ [l] ++ "."
    where (f:_) = firstName
          (l:_) = lastName

main = print (initials "Tommi" "Laine") -- "T. L."

let - in

let - in defines temporary variables in a designated scope.

cylinderSurfaceArea :: Double -> Double -> Double
cylinderSurfaceArea radius height =
    let sideArea = 2 * pi * radius * height
        topArea = pi * radius ^ 2
    in sideArea + 2 * topArea

main = print (cylinderSurfaceArea 10.0 10.0) -- 1256.6370614359173

Use let to dismantle tuples.

myTuple = (1,2,3)
main = print ((let (x,y,z) = myTuple in x + y + z) * 100) -- 600

Use let in list comprehensions.

calcBigBmis :: [(Double, Double)] -> [Double]
calcBigBmis xs = [bmi | (w,h) <- xs, let bmi = w / h ^ 2, bmi > 25.0]

Use let to create a function in a closed scope.

main = print (let square x = x * x in (square 5, square 3)) -- (25,9)

Folds

Folds apply a function to each list element, passing the result to the next element. foldl starts from the start of the list, foldr starts from the end of the list.

-- Initial value of 0 is given to the fold.
sum' :: (Num a) => [a] -> a
sum' xs = foldl (\acc x -> acc + x) 0 xs

-- `foldl` returns a function that takes a list, so we can use currying to
-- make the function shorter.
sum'' :: (Num a) => [a] -> a
sum'' = foldl (+) 0

-- Take the last element, use function on it and push it as the
-- first element of the accumalator.
map' :: (a -> b) -> [a] -> [b]
map' f xs = foldr (\x acc -> f x : acc) [] xs

main = do
    print (sum' [1,2,3])      -- 6
    print (sum'' [1,2,3])     -- 6
    print (map' (+ 1) [1,2,3]) -- [2,3,4]

fold1l and fold1r work as previous folds, but take the first or last element as the staring accumalator. Thus, they will cause a runtime error when used with an empty list.

scanl and scanr work like left fold and right fold, but return the accumalator states in a form of a list.

main = do
    print (scanl (+) 0 [1,2,3]) -- [0,1,3,6]
    print (scanr (+) 0 [1,2,3]) -- [6,5,3,0]

Normal folds may cause stack overflow if used on very long lists because the whole list is saved in memory before they are used. Module Data.List provides strict fold versions that calculate accumalator value as they go e.g. foldl'.

import Data.List (foldl')

myFoldl = foldl (+) 0 (replicate 100000000 1) -- bad
myFoldl' = foldl' (+) 0 (replicate 100000000 1)

main = do
    -- print myFoldl -- may stack overflow
    print myFoldl' -- 100000000

Maps

Association lists can be used to create mapping from keys to values.

phoneBook =
    [("betty", "555-2938")
    ,("bonnie", "452-6972")
    ,("joe", "413-5498")
    ]

Haskell also has a type dedicated to this task.

import qualified Data.Map as Map

intToGreek = Map.fromList [(1, "alpha"), (2, "beta"), (3, "gamma")]

main = do
    print (Map.lookup 1 intToGreek)  -- Just "alpha"
    print (Map.lookup 4 intToGreek)  -- Nothing
    print (Map.lookup 4 intToGreek') -- Just "delta"
        where intToGreek' = Map.insert 4 "delta" intToGreek

Functors

Functor class type tells how a function should be applied to a given type. It's usually defined that a type should be an instance of Functor if it can be mapped over.

-- Functor type class.
class Functor f where
    fmap :: (a -> b) -> f a -> f b
    -- For `a` that can be turned into `b`, when a function is applied to `a`,
    -- how do we apply it to `b`.
    -- For example, `a` can be a list, `b` can be one or multiple integers.

-- Functor implementation for Maybe.
instance Functor Maybe where
    fmap f (Just x) = Just (f x)
    fmap f Nothing = Nothing

-- Functor implementation for Tree.
instance Functor Tree where
    fmap f EmptyTree = EmptyTree
    fmap f (Node x left right) = Node (f x) (fmap f left) (fmap f right)

Applicatives

Applicative is a more advanced version of Functor. They are functors that can be applied to each other. It's in the Control.Applicative module.

class (Functor f) => Applicative f where
    pure :: a -> f a
    (<*>) :: f (a -> b) -> f a -> f b

-- Applicative implementation for Maybe.
instance Applicative Maybe where
    pure = Just
    Nothing <*> _ = Nothing
    (Just f) <*> something = fmap f something

pure is used to embed pure expressions. pure takes a value and puts it in a minimal context that still yields that value.

import Control.Applicative

main = do
    print (pure "Hey" :: [String]  )        -- ["Hey"]
    print (pure "Hey" :: Maybe String)      -- Just "Hey"

Sequential application <*>. <*> inflix function takes a Functor that has a function in it and another Functor. Then it extracts the first function and maps it over the second Functor. Note that Applicatives are also Functors.

import Control.Applicative

main = do
    print (Just (+3) <*> Just 9)            -- Just 12
    print (pure (+3) <*> Just 10)           -- Just 13
    print (Just (++"hahah") <*> Nothing)    -- Nothing
    print (pure (+) <*> Just 3 <*> Just 5)  -- Just 8
    print (pure (+) <*> Nothing <*> Just 5) -- Nothing

<$> function is basically a fmap as an infix operator. It helps wrapping a normal function as an applicative.

import Control.Applicative

main = do
    print ((++) "johntra" "volta")                      -- "johntravolta"
    print ((++) <$> Just "johntra" <*> Just "volta")    -- Just "johntravolta"

LiftA functions can be used to lift a function to actions. It's used to hide <$> and <*> syntax.

import Control.Applicative

main = do
    print ((:) <$> Just 3 <*> Just [4])     -- Just [3,4]
    print (liftA2 (:) (Just 3) (Just [4]))  -- Just [3,4]

Error Handling

myFunction = error "oh noes."
main = do
    myFunction -- "oh noes".
    myFunction -- not called because of the exception.

Sources