Learning goals (by the end)
- Explain purity, immutability, and strong static typing.
- Read and write simple functions with type annotations.
- Use pattern matching, recursion, and higher-order functions (
map,filter,fold). - Define a small algebraic data type and derive typeclass instances.
- Write a tiny
IOprogram withdonotation.
Agenda & timing (50 min)
- (0–5) What is Haskell? Tooling +
ghci - (5–15) Expressions, functions, and types
- (15–25) Pattern matching + recursion
- (25–35) Higher-order functions & list tricks
- (35–42) Algebraic data types & typeclasses
- (42–48)
IO&donotation mini-app - (48–50) Wrap-up & homework
0–5: What is Haskell? Tooling + ghci
Why Haskell - Pure (no side effects inside pure functions) - Immutable by default - Powerful type system and type inference - Concise, expressive, parallel-friendly
Quick setup
- Install via GHCup (installs GHC, cabal, stack)
- Open a terminal: ghci launches the REPL.
ghci cheats
:t expr -- show the type
:i name -- info about a function/typeclass
:r -- reload current file
:l File.hs -- load a file
:quit -- leave ghci
:set -Wall -- enable helpful warnings
5–15: Expressions, functions, and types
Everything is an expression, not a statement.
Hello, Types
-- File: Intro.hs
-- Top-level definitions need type signatures (good practice)
double :: Num a => a -> a
double x = x * 2
-- Function application is by space, not parentheses:
-- double 21 ==> 42
If/guards vs pattern matching
absVal :: (Num a, Ord a) => a -> a
absVal x
| x >= 0 = x
| otherwise = -x
Currying All functions are single-argument; “multiple parameters” are curried.
add :: Int -> Int -> Int
add a b = a + b
inc :: Int -> Int
inc = add 1 -- partial application
Immutable values
-- 'let' in ghci:
-- let y = 10
-- y = y + 1 -- ❌ not allowed; values don’t change
15–25: Pattern matching + recursion
-- Tuples and lists
first :: (a, b) -> a
first (x, _) = x
safeHead :: [a] -> Maybe a
safeHead [] = Nothing
safeHead (x:_) = Just x
Recursion
-- Factorial with pattern matching
fact :: (Eq a, Num a) => a -> a
fact 0 = 1
fact n = n * fact (n - 1)
-- Fibonacci (demo only; not efficient)
fib :: (Eq a, Num a) => a -> a
fib 0 = 0
fib 1 = 1
fib n = fib (n - 1) + fib (n - 2)
Mini exercise (3–4 min)
Implement
len :: [a] -> Intusing recursion and pattern matching.
Stretch: writesum' :: Num a => [a] -> a.
25–35: Higher-order functions & list tricks
-- map / filter / folds
squares :: Num a => [a] -> [a]
squares = map (\x -> x * x)
odds :: [Int] -> [Int]
odds = filter odd
sumSquaresOfOdds :: [Int] -> Int
sumSquaresOfOdds = foldr (+) 0 . map (^2) . filter odd
-- point-free: compose transformations
List comprehensions
pythags :: Int -> [(Int,Int,Int)]
pythags n = [ (a,b,c)
| a <- [1..n]
, b <- [a..n]
, let c = floor . sqrt . fromIntegral $ a*a + b*b
, a*a + b*b == c*c
, c <= n
]
Mini exercise (3–4 min)
Write
evensDoubled :: [Int] -> [Int]that keeps evens and doubles them.
(Try bothfilter/mapand a list comprehension.)
35–42: Algebraic data types (ADTs) & typeclasses
Define your own types
-- Product type (record syntax) + deriving common typeclasses
data Person = Person
{ name :: String
, age :: Int
} deriving (Show, Eq, Ord)
-- Sum type (either/or)
data Shape
= Circle Double
| Rect Double Double
deriving (Show, Eq)
area :: Shape -> Double
area (Circle r) = pi * r * r
area (Rect w h) = w * h
Typeclasses
- Think “interfaces of behaviour” (e.g., Eq, Ord, Show, Num).
- deriving adds default instances when possible.
Mini exercise (3–4 min)
Add a constructor
Square DoubletoShapeand extendarea.
42–48: IO & do notation mini-app
Idea: read a line of numbers, print the sum of squares of odds.
-- File: Main.hs
import Data.Char (isSpace)
trim :: String -> String
trim = f . f
where f = reverse . dropWhile isSpace
parseInts :: String -> [Int]
parseInts = map read . words . trim
sumSquaresOfOdds :: [Int] -> Int
sumSquaresOfOdds = foldr (+) 0 . map (^2) . filter odd
main :: IO ()
main = do
putStrLn "Enter numbers separated by spaces:"
line <- getLine
let nums = parseInts line
putStrLn ("Sum of squares of odds = " ++ show (sumSquaresOfOdds nums))
Explain
- main :: IO () is an effectful action.
- do sequences actions; <- binds results of actions.
- show converts values to String.
Run
ghc Main.hs && ./Main
-- or in ghci:
-- :l Main.hs
-- main
Wrap-up (48–50): key takeaways
- Pure functions + types make refactoring safe.
- Pattern matching & recursion are core.
- Higher-order functions and composition keep code concise.
- ADTs model your domain clearly.
IOis explicit — great for reasoning and testing.
Quick reference (handout)
- Function type:
f :: A -> B - List:
[a]; Tuple:(a,b); Maybe:Nothing | Just a - Common typeclasses:
Eq,Ord,Show,Read,Enum,Bounded,Num,Foldable,Functor,Applicative,Monad - Core HOFs:
map,filter,foldr,foldl'(strict),zip,(.)composition,($)low-precedence apply ghci::t,:i,:r,:l,:set -Wall
In-class checkpoints (answers ready)
len [] = 0; len (_:xs) = 1 + len xssum' [] = 0; sum' (x:xs) = x + sum' xsevensDoubled = map (*2) . filter evenSquare scase inarea:area (Square s) = s * s
Stretch ideas / homework
- Refactor
sumSquaresOfOddsto use a list comprehension. - Define
data Temperature = C Double | F Doubleand writetoC :: Temperature -> Double. - Write a small word-count program: read a file path from stdin and print number of lines/words/chars.
- Property tests (bonus): describe a property for
reverse(e.g.,reverse (reverse xs) == xs) and try QuickCheck later.