Recursion
Numeric recursion
1 | factorial 0 = 1 |
1 | factorial n = product [1..n] |
Haskell decides which function definition to use by starting at the top. So, always list multiple function definitions starting with the most specific.
Loops
Translate a loop into recursive form by making loop variables into an argument.
1 | factorial n = go n 1 |
List based recursion
1 | length :: [a] -> Int |
List II
Currying
All functions take only one argument.
map
1 | map :: (a -> b) -> [a] -> [b] |
Tricks
Dot dot notation
1
2
3
4[1..4] --= [1,2,3,4]
[2,4..8] --= [2,4,6,8]
[5,4..1] --= [5,4,3,2,1]
[1,3..8] --= [1,3,5,7]Infinite lists
1
[1..]
Lists III
Folds
foldr
1
2
3foldr :: (a -> b -> b) -> b -> [a] -> b
foldr f acc [] = acc
foldr f acc (x:xs) = f x (foldr f acc xs)
1 | : f |
foldl
1
2
3foldl :: (a -> b -> a) -> a -> [b] -> a
foldl f acc [] = acc
foldl f acc (x:xs) = foldl f (f acc x) xs
1 | : f |
foldl
is tail-recursive, the compiler will optimise it to a loop. Becase of laziness, the calls tof
will be left unevaluated, thus causing running tou of memory.
foldl'
: forces the evaluation off
immediately at each step.
Use
foldr
on lists that might be infinite or where the fold is building up a data structure. Usefoldl'
if the list is known to be finite and comes down to a single value.
foldr1
&foldl1
: taking the first element as default accumulator.Types have to be the same, and an empty list is an error.
Folds and Laziness
1 | echoes = foldr (\ x xs -> (replicate x x) ++ xs) [] |
Scans
Accumulates a values like a fold, and returns a list of all the intermediate values.
1 | scanl :: (a -> b -> a) -> a -> [b] -> [a] |
Filter
Generates a new list composed of elements that meet a condition.
1 | filter :: (a -> Bool) -> [a] -> [a] |
List comprehensions
1 | retainEven es = [n | n <- es, isEven n, ...condition] |
Type declarations
data
and constructor functions
1 | data Anniversary = Birthday String Int Int Int |
Type names and constructor functions must start with captial letters.
Deconstructing types
1 | showAnniversary :: Anniversary -> String |
Type synonyms
1 | type Name = String |
Pattern matching
In pattern matching, we attempte to match values against patterns and bind variables to successful matches.
- Constructors are allowed in patterns.
[]
&(:)
are constructors of the list.- tuple constructors:
(,)
,(,,)
…1
2(,) 5 3 --> (5,3)
(,,) 1 2 3 --> (1,2,3)
Matching literal values
1 | g :: [Int] -> Bool |
Syntax tricks
As-patterns
Binds a name to the whold value being matched: var@pattern
.
1 | contrivedMap :: ([a] -> a -> b) -> [a] -> [b] |
Records
1 | data Foo2 = Bar2 | Baz2 {bazNumber::Int, bazName::String} |
Also, the {}
pattern can be used for matching a constructor regardless of the datatype elements
1 | data Foo = Bar | Baz Int |
Where pattern matching can be used
- Equations
let
&where
- Lambda asbstractions
List comprehensions
1
2
3
4data Maybe a = Nothing | Just a
catMaybes :: [Maybe a] -> [a]
catMaybes ms = [ x | Just x <- ms ]do
blocks
Control structures
if
1 | if <condition> then <true-value> else <false-value> |
if
is an expression, and else
is mandatory.
if
can be embedding:1
g x y = (if x == 0 then 1 else sin x / x) * y
Guards
1 | f (pattern1) | predicate1 = w |
otherwise
is jst an alias to True
.
case
1 | f x = |
Controlling actions
1 | doGuessing num = do |
A note about
return
return
is not equivalent to other language,return ()
evaluates to an action which does nothing.
More on functions
let
and where
1 | addStr :: Float -> String -> Float |
let
is an expression, where
are like guards and are not expressions. let
bindings can be used within complex expressions.
where
can be incorporated into case
expressions and guards:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16describeColour c =
"This colour "
++ case c of
Black -> "is black"
White -> "is white"
RGB red green blue -> " has an average of the components of " ++ show av
-- only available for RGB
where av = (red + green + blue) `div` 3
++ ", yeah?"
doStuff :: Int -> String
doStuff x
| x < 3 = report "less than three"
| otherwise = report "normal"
where
report y = "the input is " ++ y
Lambdas
1 | sumStr = foldl (\ x str -> x + read str) 0.0 |
Operators
All function that takes two arguments and has a name consisting entirely of non-alphanumeric characters is considered an operator.
1 | 2 + 4 |
1 | (\\) :: (Eq a) => [a] -> [a] -> [a] |
Sections
1 | (2+) 4 |
“normal” prefix
Use function as a operator:1
2
3
4elem :: (Eq a) => a -> [a] -> Bool
x `elem` xs = any (==x) xs
1 `elem` [1..4]
Higher-order functions
Ord
Almost all basic data types are members of the Ord
class, which is for ordering tests.
1 | compare :: (Ord a) => a -> a -> Ordering |
->
is right-associative.
Function manipulation
Flipping arguments:
flip
1
flip :: (a -> b -> c) -> b -> a -> c
Composition:
(.)
1
(.) :: (b -> c) -> (a -> b) -> a ->c
1 | -- myInits [1,2,3] -> [[], [1,2], [1,2,3]] |
- Application:
($)
1
($) :: (a -> b) -> a -> b
$
has very low precedence.
1 | map ($ 2) [(2*), (4*), (8*)] |
curry
&uncurry
1
2
3
4
5curry :: ((a, b) -> c) -> a -> b -> c
uncurry :: (a -> b -> c) -> (a, b) -> c
-- the first element of a paire is applied to the second
uncurry ($) (reverse, "stressed")id
&const
1 | -- return its argument unchanged |
Using GHCi effectively
Tab completion
:commands
:h[elp]
:l[oad]
:r[eload]
:t[ype]
:m[odule]
:b[rowse]
Timing funcitions
The basic way to measure how much a function takes to run. Run :set + s
then run the functions that are tested.
Multi-line input
Surrounded code with :{
:}
.