NB: :{
and :}
to paste blocks into GHCi.
- Every expression has a type known at compile time
- Examine a type with
:t
- Common types: Int, Integer, Float, Double, Bool, Char
- functions have types e.g.,
addThree
- Type variables
:t head
or:f fst
head :: [a] -> a
-head
is a function that takes a list ofa
s, and evaluates to ana
. There is no constraint on whata
can be, so we know we can put anything in a list, but it has to be the same anything.
- Type classes: behaviour or interface.
- E.g.,
:t (==)
- Anything before
=>
is a class constraint Eq
(for = and /=)Ord
e.g.,:t (>)
...- We make our own typeclasses in Chapter 8
- but... https://www.schoolofhaskell.com/user/garrett.mitchener/Type%20classes gives an idea of what that is like: the
class
syntax defines the "interface", and then implementations are provided for specific types, such asEq
forInt
.
- When defining functions, you can define separate function bodies for different patterns
- First one to match wins
- e.g.,
lucky 7
example - Non-exhaustive patterns produce runtime errors
- List pattern matching: [1,2,3] vs 1 : 2 : 3 : []
- Example our own version of head, perhaps using
error
- Example of
tell
to describe a list. - "as patterns
(
@), the example being the first letter of
allis x in
all@(x:xs)`
E.g.,
max' a b
| a > b = a
| otherwise = b
We saw that otherwise
is defined as True
!
cylinder' r h =
sideArea + 2 * topArea
where
sideArea = 2 * pi * r * h
topArea = pi * r ^ 2
Syntax only used in defining functions. Whereas...
Let is an expression so can be used anywhere you need an expressions. The syntax is let...in
. Examples:
[ let square x = x * x in ((square 1), (square 4)) ]
...produces [ (1,16) ]
.
cylinder r h =
let sideArea = 2 * pi * r * h
topArea = pi * r ^ 2
in sideArea + 2 * topArea
Let can also be used without an in
in for comprehensions:
{- revisit when we get to do notation -}
[ square x | x <- [1..3], let square x = x*x ]
For example (spoiler for exercise below!):
safetail xs = case null xs of
True -> []
False -> tail xs
Can be used anywhere you need an expression.
Consider a function
safetail :: [a] -> [a]
that behaves as the library funtion tail, except that safe tail maps the empty list to itself, whereas tail produces an error in this case.
Define safetail using:
- (a) a conditional expression
- (b) guard equations
- (c) pattern matching
Hint: make use of the library function null
.
Define a function contains
that returns True
if the value is in the list. E.g.,
contains 6 [1,2,3] -- False!
contains "cat" ["dog", "cat"] -- True!
contains True [False, False, True] -- True!
But write the type signature first.