Lecture 1.0 — 2017-01-23

Introduction

This lecture is written in literate Haskell; you can download the raw source.

After the usual administrivia and introductions, we talked briefly about craft before diving in to Haskell. We heard the following quote:

To understand craftsmanship, we must ask not only “What has been made?” and “How has this been made?” but also “Who made this and why?” Craftsmanship is a relationship between the maker and the process of creation. It is not simply a set of skills one acquires, like the ability to read or drive a car. More than anything, craftsmanship is a matter of attitude: why we choose to devote time to such a demanding endeavor, why we choose to make a certain object of a certain appearance, and how we go about it.
—Peter Korn, Woodworking Basics: Mastering the Essentials of Craftsmanship
import Prelude hiding ((.))

Then we finally got started. We wrote a simple arithmetic function:

mean :: Int -> Int -> Int
mean x y = div (x + y) 2

And then we learned about using backticks to make functions infix operators:

mean' :: Int -> Int -> Int
mean' x y = x + y `div` 2

…and after figuring out how to use ghci to test our program, we wrote some more interesting, recursive functions:

fact :: Int -> Int
fact 0 = 1
fact n = n * fact' (n - 1)

We learned about conditionals:

fact' :: Int -> Int
fact' n = if n <= 0
          then 1
          else n * fact' (n - 1)

And the pipe form for conditionals within a top-level pattern match:

factPat :: Int -> Int
factPat :: Int -> Int
factPat n | n <= 0 = 1
factPat n = n * factPat (n - 1)

And we saw some pretty bad code. It’s redundant and poorly typeset.

fact'' :: Int -> Int
fact'' 0 = 1
fact'' 1 = 1
fact'' 2 = 2
fact'' n = if n < 0 then 1 
           else n * fact'' (n - 1)

We saw some more interesting base cases:

fib :: Int -> Int
fib 0 = 1
fib 1 = 1
fib n = fib (n - 1) + fib (n - 2)

And we wrote an “accumulator passing” function, using a where clause to nest a local definition. Can you see how fibAux is like a loop?

fib' :: Int -> Int
fib' n = fibAux 1 1 n
  where fibAux f2 f1 0 = f2
        fibAux f2 f1 n = fibAux f1 (f1+f2) (n-1)

To see how important type annotations are to getting good error messages, try removing the type annotation on fibAux and forgeting to apply fibAux in the third case.

Then we defined our first datatype, representing the days of the week.

data Day =
    Sunday
  | Monday
  | Tuesday
  | Wednesday
  | Thursday
  | Friday
  | Saturday

We wrote a simple function to determine whether or not it’s party time; note how the wildcard pattern _ doesn’t bind any names and matches anything.

isWeekend :: Day -> Bool
isWeekend Saturday = True
isWeekend Sunday = True
isWeekend _ = False

Top-level definitions in Haskell automatically do pattern matching, but we can be explicit if we like:

isWeekend' :: Day -> Bool
isWeekend' d = 
  case d of
    Saturday -> True
    Sunday -> True
    _ -> False

I think isWeekend'’s definition is slightly less good than isWeekend: why do an expression-level match when we could have matched at the top-level?

We defined isWeekday in terms of a function we already had:

isWeekday :: Day -> Bool
isWeekday d = not (isWeekend d)

But we can also use the math-like composition operator:

isWeekday' :: Day -> Bool
isWeekday' = not . isWeekend

As a class we managed to guess (.)’s type, and then we wrote the definition:

(.) :: (b -> c) -> (a -> b) -> (a -> c)
f . g = \x -> f (g x)

Finally, we saw that booleans aren’t builtins in Haskell. Somewhere in the Prelude (the standard definition), someone defined the booleans:

data Bool = False | True

And we could even define our own conditional:

myIf :: Bool -> a -> a -> a
myIf True  t e = t
myIf False t e = e

factMine :: Int -> Int
factMine n = myIf (n <= 0) 1 (n * fact (n - 1))

Just at the end, we started talking about linked lists, defining our own like so:

data List a =
  Nil
  | Cons a (List a)
  deriving Show