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)

  1. Create a folder where GHCup will live (pick a drive with space), e.g.:

    • C:\ghcup\bin
  2. Download the GHCup executable for Windows and place it in that folder. (The official page provides the current download link and naming.)

  3. 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
  1. 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)

  1. Run the official bootstrap script (interactive installer):
  • curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh
  1. The installer may offer to update your shell PATH automatically; accept if you want it to “just work” in new terminals.

  2. Open a new terminal window (or reload your shell config) so your PATH changes take effect.

C) Verify everything works (all platforms)

Run:

  • ghc --version
  • runghc --version
  • cabal --version

If those commands resolve and print versions, the toolchain is installed correctly.

D) Quick sanity test (hello world)

  1. Create Main.hs:
main :: IO ()
main = putStrLn "Hello Haskell"
  1. 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

Core Concepts

Advanced & Tools

Industry & Optimization

Research & Guides

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 xs

Matrix 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) a

Count 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 == 0

Occurence

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 q

Perfect 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)

0 items under this folder.