Lecture 1 — 2015-09-02
Introduction, learning Haskell
This lecture is written in literate Haskell; you can download the raw source.
module Lec01 where
We went over the syllabus and we all filled out some notecards, with the following information:
- Name
- Have you taken 81?
- Favorite programming language
- Least favorite programming language
- Spoken languages
- Favorite music right now
- X or Y (name a dialectic and pick a side)
- Other interests
As a result, I’ll have to google for what “trap” music is.
After some tooling around in GHCi, the interpreter, we started writing some real code. An easy arithmetic function…
mean :: Int -> Int -> Int
mean x y = (x + y) `div` 2
…followed by some more interesting recursive functions:
fact :: Int -> Int
fact 0 = 1
fact n = n * (fact (n - 1))
fib :: Int -> Int
fib 0 = 1
fib 1 = 1
fib n = fib (n-1) + fib (n-2)
Then we defined our datatype, discovering along the way that Haskell doesn’t automatically give us things we can show
or compare for equality (via ==
. The deriving
clause lets us get these things.
data Day =
Sunday
| Monday
| Tuesday
| Wednesday
| Thursday
| Friday
| Saturday
deriving (Show,Eq)
We defined isWeekday two different ways. First we had:
isWeekday' :: Day -> Bool
isWeekday' Saturday = False
isWeekday' Sunday = False
isWeekday' _ = True
and then we saw what happens when you drop off pattern match (an error!). We also looked at the case
statement:
isWeekday :: Day -> Bool
isWeekday d =
case d of
Saturday -> False
Sunday -> False
_ -> True
And then we defined functions using other functions. A breeze.
isWeekend :: Day -> Bool
isWeekend d = not (isWeekday d)
This one was boring.
nextDay :: Day -> Day
nextDay Monday = Tuesday
nextDay Tuesday = Wednesday
nextDay Wednesday = Thursday
nextDay Thursday = Friday
nextDay Friday = Saturday
nextDay Saturday = Sunday
nextDay Sunday = Monday
But this was more interesting! We get to use a math-like syntax instead of conditionals.
weekdays :: [Day] -> [Day]
weekdays [] = []
weekdays (d:ds)
| isWeekday d = d:weekdays ds
| otherwise = weekdays ds
We talked a bit more about lists, defining our own version of the library function last
.
last' :: [a] -> a
last' [] = error "don't do that"
last' [x] = x
last' (x:xs) = last' xs
To tie everything together, we worked on some functions for thinking about points and lines on the Cartesian plane.
data Point = Point { xCoord :: Float,
yCoord :: Float }
deriving Show
data Line =
Vert Float
| Sloped { lineSlope :: Float,
lineIntercept :: Float }
deriving Show
We went through a couple versions of a function that takes two points and returns a line going them. First, we had:
mkLine' :: Point -> Point -> Line
mkLine' p1 p2 =
if xCoord p1 == xCoord p2
then Vert $ xCoord p1
else
let m = (yCoord p1 - yCoord p2) /
(xCoord p1 - xCoord p2) in
let b = yCoord p1 - (m * xCoord p1) in
Sloped m b
But then Eric pointed out that this had a funny behavior when p1
and p2
are equal. First we established that you can’t write something like:
mkLine p1 p1 = {- error case where the points are the same -}
mkLine p1 p2 = {- normal function -}
Rather than having the function call error
, we decided to use the Maybe
datatype. By now we had already used the :i [type name]
and :t [expression]
commands in GHCi to look at types. Running :i Maybe
, we got:
data Maybe a = Nothing | Just a -- Defined in ‘GHC.Base’
-- ... and a bunch of instances we don't really care about right now
We then simply changed the type on mkLine
to return Maybe Line
, and then the type checker guided us to all the spots we needed to fix.
mkLine :: Point -> Point -> Maybe Line
mkLine p1 p2 =
if xCoord p1 == xCoord p2
then if yCoord p1 == yCoord p2
then Nothing
else Just $ Vert $ xCoord p1
else
let m = (yCoord p1 - yCoord p2) /
(xCoord p1 - xCoord p2) in
let b = yCoord p1 - (m * xCoord p1) in
Just $ Sloped m b