Data
π Pattern β specific value or shape of input.
f :: type
f ... = case var of
    pat1 -> result1
    pat2 -> result2
    ...
    patN -> resultNf :: type
f pat1 = result1
f pat2 = result2
...
f patN = resultNnot :: Bool -> Bool
not True  = False
not False = TrueisZero :: Int -> Bool
isZero 0 = True
isZero n = Falseeval :: Char -> Int -> Int -> Int
eval op x y = case op of
    '+' -> x + y
    '-' -> x - y
    '*' -> x * y
    '/' -> div x y
    _ -> 0    ^ watch for indentation!isEmpty :: [Int] -> Bool
isEmpty [] = True
isEmpty _  = FalsesumOfTwoInThree :: [Int] -> Int
sumOfTwoInThree [x, _, y] = x + y
sumOfTwoInThree _         = 0oneOrTwoZeroes :: [Int] -> Bool
oneOrTwoZeroes l = case l of
    [0]    -> True
    [0, 0] -> True
    _      -> False-- check if a list has at least two elements
atLeastTwo :: [Int] -> Bool
atLeastTwo [_, _] = True
atLeastTwo _      = FalseDoes the following work?
β No! Because it checks only on lists of size two.
[]      
x : xs1οΈβ£ Empty list
2οΈβ£ An element prepended to a list
      βββ connecting head and tail
      β   `:` is both operator and pattern
      β
    x : xs
    β   β
    β   βββ tail
  headWait! What about list literals [x, y, z]?
Syntax sugar π¬
[ 3 ,  1 , 2 ]
  3 :  1 :  2 : []
  3 : (1 : (2 : []))headOrDef :: Int -> [Int] -> Int
headOrDef def []      = def
headOrDef _   (x : _) = xdropHead :: [Int] -> [Int]
dropHead []       = []
dropHead (_ : xs) = xssecondIsZero :: [Int] -> Bool
secondIsZero (_ : 0 : _) = True
secondIsZero _ = Falsesum :: [Int] -> Int
sum      []  = 0
sum (x : xs) = x + sum xsβ οΈ Warning!
π Slow implementation!
count :: Int -> [Int] -> Int
count n list = go 0 list
  where
    go :: Int -> [Int] -> Int
    go acc list =
        if null list
        then result
        else if head list == n
        then go (acc + 1) (tail list)
        else go  acc      (tail list)count :: Int -> [Int] -> Int
count n list = go 0 list
  where
    go :: Int -> [Int] -> Int
    go acc [] = acc
    go acc (x : xs)
        | x == n    = go (acc + 1) xs
        | otherwise = go  acc      xsπ©βπ¬ Always use pattern matching on lists instead of unsafe
head and tail functions whenever you need either of them.
isEmpty :: [Int] -> True
isEmpty _  = False
isEmpty [] = Truesame :: Int -> Int -> True
same x x = True
same _ _ = FalseheadOrDef :: Int -> [Int] -> Int
headOrDef def l       = def
headOrDef _   (x : _) = xπ©βπ¬ Incorrect order of patterns
π©βπ¬ Variable is a "catch-all" pattern
π©βπ¬ Patterns on variable names are not supported
head :: [Int] -> Int
head (x : _) = xπ©βπ¬ Patterns don't cover all possible cases
isGreeting :: String -> Bool
isGreeting str = case str of
    'H' : 'i' : '!' : _ -> True
    _ -> Falseπ©βπ¬ No errors here,
just looks ugly.
example :: Bool -> [Int] -> Int
example True  []     = 0
example False [x, y] = x + yπ A function is total if it is defined for all inputs of its corresponding type, or in other words, if a function returns the output on any possible values of the input types.
βοΈ GHC checks if a function is partial due to non-exhaustive patterns.
π Partial β non-total.
<interactive>:32:1: warning: [-Wincomplete-patterns]
    Pattern match(es) are non-exhaustive
    In an equation for βexampleβ:
        Patterns not matched:
            True (_:_)
            False (_:_:_:_)
            False [_]
            False []ghci> :t ('x', True)
('x', True) :: (Char, Bool)
ghci> :t ([True, False], "abc", 'F')
([True, False], "abc", 'F') :: ([Bool], [Char], Char)ghci> fst ('x', True)
'x'
ghci> snd ('x', True)
True
ghci> fst (3, True, False)
<interactive>:39:5: error:
    β’ Couldn't match expected type β(a, b0)β
                  with actual type β(a0, Bool, Bool)β
splitAtPos3 :: [Int] -> ([Int], [Int])
splitAtPos3 l = (take 3 l, drop 3 l)ghci> splitAtPos3 [1..10]
([1,2,3],[4,5,6,7,8,9,10])
ghci> splitAtPos3 [3, 1]
([3,1],[])showTriple :: (Bool, Int, String) -> String
showTriple (b, n, string) =
    if b
    then "The number is: " ++ show n
    else "The string is: " ++ stringghci> showTriple (True, 42, "hello")
"The number is: 42"
ghci> showTriple (False, 42, "hello")
"The string is: hello"ADT β a type formed by combing other types using either
product or sum types.
Product type
Sum type
Combining types using both
Choice of types, either one or another
Product type β zero or more types combined
String
Int
Bool
"Hello!"42True("Hello!", 42, True)
("Byeeee", 15, False)
("", 0, True)All possible combinations including values of all types
      βββ data type name
      β
      β       βββ Constructor name
      β       β
data User = MkUser String Int Bool
                     β     β    β
                     βββββββΌβββββ
                           β
                           β
                      Field typesdata User = MkUser String Int Bool
    deriving (Show)  -- to display our type in GHCigetUserName :: User -> String
getUserName (MkUser name _ _) = namesetUserName :: String -> User -> User
setUserName name (MkUser _ age isTired) = MkUser name age isTiredghci> :t MkUser
MkUser :: String -> Int -> Bool -> User
ghci> getUserName (MkUser "John" 29 True)
"John"
ghci> setUserName "Ivan" (MkUser "John" 29 True)
MkUser "Ivan" 29 TruegetUserAge :: User -> Int
getUserAge (MkUser _ age _) = ageghci> john = MkUser {userAge = 29, userIsTired = True, userName = "John"}
ghci> john
MkUser {userName = "John", userAge = 29, userIsTired = True}
ghci> userName john
"John"
ghci> ivan = john { userName = "Ivan", userIsTired = False }
ghci> ivan
MkUser {userName = "Ivan", userAge = 29, userIsTired = False}data User = MkUser
    { userName    :: String
    , userAge     :: Int 
    , userIsTired :: Bool
    } userName    :: User -> String
userAge     :: User -> Int
userIsTired :: User -> BoolGenerates the following top-level functions
β Initialization using names
β Record-update syntax
Sum type β choice of zero or more types
String
Int
Bool
"Hello!"42True"Hello!"
42
TrueAny possible option of a value from each type
data Color
    = Red
    | Green
    | Bluedata Bool
    = False
    | TrueshowColor :: Color -> String
showColor color = case color of
    Red   -> "red"
    Green -> "green"
    Blue  -> "blue"ghci> showColor Blue
"blue"
ghci> map showColor [Red, Green, Blue, Green]
["red","green","blue","green"]data Result
    = Error String
    | Ok Intdivide :: Int -> Int -> Result
divide _ 0 = Error "Division by zero!"
divide x y = Ok (div x y)ghci> showResult (divide 15 0)
"Error: Division by zero!"
ghci> showResult (divide 15 3)
"Ok: 5"showResult :: Result -> String
showResult (Error msg) = "Error: " ++ msg
showResult (Ok result) = "Ok: " ++ show resultA data type with 2 constructors
Each constructor has one field
data Answer
    = NoResult
    | Result Intdata Property
    = Padding Int
    | Clickable Bool Int
    | Description Stringdata IntList
    = Empty
    | Cons Int IntListlength :: IntList -> Int
length Empty = 0
length (Cons _ xs) = 1 + length xs1οΈβ£ Empty list
2οΈβ£ An element prepended to a list
nzeroes :: Int -> IntList
nzeroes 0 = Empty
nzeroes n = Cons 0 (nzeroes (n - 1))ghci> nzeroes 3
Cons 0 (Cons 0 (Cons 0 Empty))
ghci> length (nzeroes 126)
126type MyTriples = [(Int, Bool, String)]
type IntPredicate = Int -> BoolβΉοΈ Use the type keyword to giveΒ another name to an existing type
type String = [Char]
type FilePath = StringStandard type aliases
β οΈ The type is the same! It's just a different name.
type Attack  = Int
type Defense = Int
type Health  = Intdamage :: Attack -> Defense -> Health -> Health
damage atk def hp = hp + def - atknewtype β lightweight wrapper for an existing type. Can only have:
type Attack  = Int
type Defense = Int
type Health  = Intnewtype Attack  = MkAttack Int
newtype Defense = MkDefense Int
newtype Health  = MkHealth Intdamage :: Attack -> Defense -> Health -> Health
damage (MkAttack atk) (MkDefense def) (MkHealth hp) =
    MkHealth (hp + def - atk)π° Benefits
π° Costs
dup :: a -> (a, a)
dup x = (x, x)β¬οΈ Specific types start with an upper letter: String, Int, Bool, ...
β¬οΈ Type variables start with a lower letter: a, b, c, f, m, ...
ghci> dup 'x'
('x','x')
ghci> :t dup 'x'
dup 'x' :: (Char, Char)
ghci> dup True
(True,True)
ghci> :t dup True
dup True :: (Bool, Bool)fst :: (a, b) -> a
snd :: (a, b) -> b
(++) :: [a] -> [a] -> [a]
(:)  ::  a  -> [a] -> [a]
head    :: [a] ->  a
tail    :: [a] -> [a]
reverse :: [a] -> [a]
take :: Int -> [a] -> [a]
drop :: Int -> [a] -> [a]
zip :: [a] -> [b] -> [(a, b)]map    :: (a -> b)    -> [a] -> [b]
filter :: (a -> Bool) -> [a] -> [a]Hoogle β Haskell search engine to search Haskell package database by name, module or even type.
π A treasure chest always contains gold and some reward
data Chest a = MkChest
    { chestGold     :: Int
    , chestTreasure :: a
    }type BronzeChest = Chest Armor
type SilverChest = Chest (Sword, Armor)
type GoldenChest = Chest (Artifact, Sword, [Gemstone])ghci> :t MkChest
MkChest :: Int -> a -> Chest a
ghci> MkChest 100 True
MkChest { chestGold = 100
        , chestTreasure = True}data RewardChest
    = Bronze BronzeChest
    | Silver SilverChest
    | Golden GoldenChestreward :: Dragon -> RewardChest
reward ... = ...data Maybe a
    = Nothing
    | Just adata Either a b
    = Left a
    | Right bβΉοΈ Optional value
βΉοΈ Choice between two types
βΉοΈ List
data [] a
    = []
    | a : [a]data List a
    = Empty
    | Cons a (List a)Simpler version (not in base)
find :: (a -> Bool) -> [a] -> Maybe a
ghci> find (> 4) [3, 1, 2]
Nothing
ghci> find (> 4) [3, 5, 1, 2, 10]
Just 5
βΉοΈ Find an optional value
fromMaybeInt :: Maybe Int -> Int
fromMaybeInt Nothing  = 0
fromMaybeInt (Just n) = nβΉοΈ Optional number to zero default
βΉοΈ Lists of different types
partitionEithers :: [Either a b] -> ([a], [b])ghci> partitionEithers [Left 3, Right "hi!", Left 5]
([3,5],["hi!"])showInt :: Int -> String
showInt n = show nshowInt :: Int -> String
showInt = showonlyEven :: [Int] -> [Int]
onlyEven xs = filter even xsonlyEven :: [Int] -> [Int]
onlyEven = filter evenprod :: [Int] -> [Int] -> [Int]
prod xs ys = zipWith (*) xs ysprod :: [Int] -> [Int] -> [Int]
prod = zipWith (*)ghci> :t zipWith
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
ghci> zipWith (*) [3, 1, 2] [10, 20, 30]
[30,20,60]π©βπ¬ You don't always want to eta-reduce because variable names can be helpful in some situations
βοΈ Function composition operator: the dot (.) operator
(.) :: (b -> c) -> (a -> b) -> a -> c
(.) f g = \x -> f (g x)(.) :: (b -> c) -> (a -> b) -> a -> c
(.) f g = \x -> f (g x)ghci> (length . show) True
4
ghci> map (length . words) ["Hi all", "bi dup boo", "x"]
[2,3,1]
ghci> map (even . length . words) ["Hi all", "bi dup boo", "x"]
[True,False,False]π A function that returns first 5 lists of even length
takeEven5 :: [[a]] -> [[a]]
takeEven5 list = take 5 (filter (\l -> even (length l)) list)1οΈβ£ Rewrite lambda to composition
takeEven5 list = take 5 (filter (\l -> (even . length) l) list)2οΈβ£ Eta-reduce lambda
takeEven5 list = take 5 (filter (even . length) list)3οΈβ£ Rewrite main function to composition
takeEven5 list = (take 5 . filter (even . length)) listtakeEven5 = take 5 . filter (even . length)4οΈβ£ Eta-reduce main function