Functional programming L+Pr / IP-18fFUNPEG
This class isn’t “just writing Haskell syntax”. It’s basically training a different way of thinking.
- Course code: IP-18fFUNPEG
- Credits: 6
Setup (what you install + what you run)
You’ll use GHC (the Haskell compiler) and usually run files either by interpreting them or compiling them.
1) Install GHC using GHCup (proper tutorial)
GHCup is the standard toolchain manager for Haskell: it can install and manage GHC, cabal, and optionally stack/HLS.
A) Windows (native)
-
Create a folder where GHCup will live (pick a drive with space), e.g.:
C:\ghcup\bin
-
Download the GHCup executable for Windows and place it in that folder. (The official page provides the current download link and naming.)
-
Open a terminal and run the basic installs (recommended flow from the official instructions):
- Install a recommended GHC and set it active:
ghcup install ghc
- Install cabal:
ghcup install cabal latest
- (Optional) install stack:
ghcup install stack latest
- (Optional) install Haskell Language Server:
ghcup install hls latest
- If the Windows setup asks for MSYS2 updates, run the update command described in the official Windows section (this avoids broken toolchain dependencies):
ghcup run -m -- pacman --noconfirm -Syuu
B) Linux / macOS (and WSL)
- Run the official bootstrap script (interactive installer):
curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh
-
The installer may offer to update your shell PATH automatically; accept if you want it to “just work” in new terminals.
-
Open a new terminal window (or reload your shell config) so your PATH changes take effect.
C) Verify everything works (all platforms)
Run:
ghc --versionrunghc --versioncabal --version
If those commands resolve and print versions, the toolchain is installed correctly.
D) Quick sanity test (hello world)
- Create
Main.hs:
main :: IO ()
main = putStrLn "Hello Haskell"- Run it:
runghc Main.hs
If it prints Hello Haskell, you’re ready for labs.
Syllabus (what you actually learn)
Topics you need to learn:
- Defining functions, guards, recursion, elementary types
- Syntax, reduction steps, normal form, lazy vs eager evaluation
- Standard functions, predefined operators
- Data structures: lists, tuples, records, arrays
- List processing: definitions, generators, patterns, recursion
- Types: simple, algebraic, composite, trees
- Algorithms: search, sort, traverse
- Higher-order functions:
map,filter,iterate,fold - Tuples, list comprehensions, equivalences
- Sorting, merging
- Infinite lists
- Algebraic types, trees, ADTs
- Recursions
What to target
- List Comprehension: once List Comprehension clicks, half the course becomes easier.
- Recursion
Haskell Learning Resources
These resources from the Functional Programming syllabus provide curated materials for Haskell study, ranging from beginner guides to advanced topics.
Beginner Tutorials
- Discourse: Best Haskell Materials 2024 – Community-curated top resources.
- Haskell Books Index – 77+ books tagged by level and topic.
- code.world (9 resources) & code.world/haskell (29 hands-on sites).
- Haskell Wiki & Wikibooks Haskell – Core references (not identical).
Core Concepts
- Numeric Types in Haskell.
- Typeclassopedia – Type classes explained.
- Strictness & Laziness.
Advanced & Tools
- Haskell Ecosystem Overview.
- GHC User Guide, Cabal Docs, Stack Docs – “Secret knowledge for wizards.”
- Prelude Docs & Base Package.
Industry & Optimization
- Haskell in Industry & Haskellcosm.
- HS-Opt Handbook – Optimization (12 resources).
- Haskell Foundation Projects & Podcast.
Research & Guides
- FP Complete Syllabus & Learn Path.
- Sunsetting “What I Wish I Knew” & Sources.
- Functional Pearls & PCPH.
Haskell Learning Guide
List Operations
Sort high to low
xs = [2, 3, 4, 6]
ys = sortOn Down xs -- [6, 4, 3, 2]Sort low to high
ys = sort xs -- [2, 3, 4, 6]Return odd indices (1,3,5…)
ys = [ x | (i, x) <- zip [0..] xs, odd i ] -- [3, 6]Return even indices (0,2,4…)
ys = [ x | (i, x) <- zip [0..] xs, even i ] -- [2, 4]Adjacent pairs (overlapping)
ys = zip xs (tail xs) -- [(2,3),(3,4),(4,6)]Non-overlapping pairs
pairs2 (x:y:rest) = (x,y) : pairs2 rest
pairs2 _ = [] -- [(2,3),(4,6)]All prefixes
ys = [take i xs | i <- [1..length xs]] -- [[2,3],[2,3,4],[2,3,4,6]]Int to List
import Data.Char
digits = map digitToInt(show(abs digit)) -- 1234 -> [1,2,3,4]List Comprehensions
Filter with condition
[ (a,b) | (a,b) <- pairs, a `rem` b == 0 ]Multiple generators + guards
[ (a,b) | (i,a) <- zip [0..] ys, (j,b) <- zip [0..] ys, i < j, a `rem` b == 0 ]Cartesian product
[ (x,y) | x <- [1..3], y <- [4..6] ]Higher-Order Functions
Map with index
mapWithIndex f xs = [ f i x | (i,x) <- zip [0..] xs ]Filter then map (selectiveMap)
selectiveMap p f xs = [ if p x then f x else x | x <- xs ]Common Patterns
Attach indices
zip [0..] xs -- [(0,2),(1,3),(2,4),(3,6)]All unique pairs (i < j)
[ (a,b) | (i,a) <- zip [0..] ys, (j,b) <- zip [0..] ys, i < j ]Divisible pairs
divisiblePairs xs = [ (a,b) | (i,a) <- zip [0..] ys, (j,b) <- zip [0..] ys,
i < j, a `rem` b == 0 ]
where ys = sortOn Down xsMatrix sum of squared differences
ssd xs ys = sum [ (a-b)^2 | (rowX,rowY) <- zip xs ys, (a,b) <- zip rowX rowY ]Midterm/Endterm Patterns
Count odd digits
countOdds n = show $ length $ filter odd $ digits n
where digits 0 = []
digits x = map read (show x)Common elements (unique)
import Data.List
commonElements xs ys = nub [x | x <- xs, x `elem` ys]Remove numbers from list
removeDiv5 :: [Int] -> [Int]
removeDiv5 = filter (\x -> x `mod` 5 /= 0)Arithmetic Sequence
-- Write a function which takes `a` as a first term,
-- `d` as a common difference, and `n` as the number of terms
-- in the arithmetic sequence, and returns the arithmetic sequence.
arithmeticSequence :: Int -> Int -> Int -> [Int]
arithmeticSequence a d n = take n $ iterate (+d) aCount Vowels
-- Write a function, that counts the number of vowels
-- (a, e, i, o, u) in a given string.
-- The function should be case insensitive.
countVowels :: String -> Int
countVowels = length . filter (`elem` "aeiouAEIOU")Prime check
isPrime :: Int -> Bool
isPrime x = divisors x == [1,x]
where
divisors n = filter (`divisible` n) [1..n]
divisible d n = rem n d == 0Occurence
replaceWithOccurrence :: [Int] -> [Int]
replaceWithOccurrence xs = map (\x -> count x xs) xs
where
count y = length . filter (== y)Dec to Bin
toBinary :: Int -> String
toBinary 0 = "0"
toBinary k = reverse (helper k)
helper :: Int -> String
helper 0 = []
helper y = let (q,r) = y `divMod` 2 in (if r == 0 then '0' else '1') : helper qPerfect numbers
perfectNumbers n = [x | x <- [2..n], sum (init (divisors x)) == x]Student averages
averageScores = map (\(name, scores) -> (name, sum scores / genericLength scores))Numeric helpers
-- Dot product of two Int lists
dotProd :: [Int] -> [Int] -> Int
dotProd xs ys = sum [ x * y | x <- xs | y <- ys ]
-- Average of a list (generic numeric)
avg :: (Real a, Fractional b) => [a] -> b
avg xs = realToFrac (sum xs) / fromIntegral (length xs)Parallel list comprehensions
{-## LANGUAGE ParallelListComp ##-}
-- v1 + v2 * v3, elementwise
mulAddA :: [Int] -> [Int] -> [Int] -> [Int]
mulAddA v1 v2 v3 = [ a + b * c | a <- v1 | b <- v2 | c <- v3 ]
-- Elementwise product and safe division
elementWise :: [Double] -> [Double] -> ([Double], [Double])
elementWise xs ys = (products, divisions)
where
n = min (length xs) (length ys)
xs' = take n xs
ys' = take n ys
products = [ x * y | x <- xs' | y <- ys' ]
divisions = [ if y == 0 then 0 else x / y
| x <- xs' | y <- ys' ]Tuples and Booleans
-- Combine tuples with Int and Bool
processTuples :: [(Int,Bool)] -> [(Int,Bool)] -> Int
processTuples xs ys =
sum [ if b1 && b2 then a1 + a2 else a1 - a2
| (a1,b1) <- xs
| (a2,b2) <- ys ]Records and updates
data Univ = Univ { uniName :: String, uniID :: Int } deriving (Show, Eq)
data Student = Student
{ neptunID :: Int
, studentName :: String
, uni :: Univ
, grades :: [Int]
} deriving Show
-- Rename a university for all its students (by uniID)
renameUniversity :: Univ -> String -> [Student] -> [Student]
renameUniversity target newName =
map (\s ->
if uniID (uni s) == uniID target
then s { uni = (uni s) { uniName = newName } }
else s)
-- Students with at least one grade > 90
outstandingStudents :: [Student] -> [String]
outstandingStudents sts =
[ studentName s | s <- sts, any (>90) (grades s) ]Trees
-- Simple binary tree
data Tree a = Node a (Tree a) (Tree a) | Leaf
deriving Show
-- Swap left and right subtrees
swapSubTree :: Tree a -> Tree a
swapSubTree Leaf = Leaf
swapSubTree (Node x l r) = Node x (swapSubTree r) (swapSubTree l)Expression trees to infix string
data ExpressionTree a
= Operand a
| Operator String (ExpressionTree a) (ExpressionTree a)
deriving Show
evaluate :: ExpressionTree String -> String
evaluate (Operand x) = x
evaluate (Operator op l r) = "(" ++ evaluate l ++ op ++ evaluate r ++ ")"Algebraic data types and pattern matching
data Color = Red | Blue | Yellow | Purple | Orange | Green
deriving (Show, Eq)
combineColors :: Color -> Color -> Color
combineColors Red Blue = Purple
combineColors Blue Red = Purple
combineColors Red Yellow = Orange
combineColors Yellow Red = Orange
combineColors Blue Yellow = Green
combineColors Yellow Blue = Green
combineColors c1 c2
| c1 == c2 = c1
| otherwise = error "Unsupported combo"Type class instances
data Team = Team { teamName :: String, teamID :: Int }
data Player = Player
{ playerID :: Int
, playerName :: String
, playerTeam :: Team
, playerScores :: [Int]
}
-- Custom Show
instance Show Team where
show t =
"Team teamName " ++ show (teamName t) ++
", teamID " ++ show (teamID t)
instance Show Player where
show p =
"Player ID " ++ show (playerID p) ++
", Name " ++ playerName p ++
", Team " ++ show (playerTeam p) ++
", Scores " ++ show (playerScores p)
-- Custom Eq: same total score, different team
instance Eq Player where
p1 == p2 =
sum (playerScores p1) == sum (playerScores p2) &&
teamID (playerTeam p1) /= teamID (playerTeam p2)