Functional programming

About me

  • 2nd year Software Science
  • High tech systems track
  • President AEGEE-Eindhoven

Things I ♥

  • Travelling
  • Learning
  • Food
  • (functional) programming
  • Kabouter Wesley

Today's project

avg-grade

  • Displays average grade according to Canvas
  • (Gives either advice or compliment)

Structure

  • Display average grade according to Canvas
  • Give either advice or compliment
  1. Input
  2. Transformation
  3. Output
  • Get grades from Canvas
  • Calculate average
  • Display average, advice/compliment

"Pipeline"

Web server

  1. Input
  2. Transformation
  3. Output
  • Receive URL request, database
  • Generate page
  • Send page

Real life example

Transformation

  • Calculate the output,
  • based on a rule,
  • from the input
f(x) = x + 1
f(x)=x+1f(x) = x + 1

e.g. output = input + 1

Sounds familiar?

Math → 

f :: Integer -> Integer
f x = x + 1
f(x) = x + 1
f(x)=x+1f(x) = x + 1

Math → 

add :: Integer -> Integer -> Integer
add x y = x + y
add(x, y) = x + y
add(x,y)=x+yadd(x, y) = x + y
add :: Integer, Integer -> Integer
add x y = x + y

Types

add :: Integer -> Integer -> Integer
add x y = x + y

-- We can use add like so:
add 1 2      -- output: 3

-- But also like this:
(add 1) 2    -- output: 3
-- The type of (add 1) is: Integer -> Integer

f : Integer -> Integer
f = add 1
-- Instead of f x = x + 1

Partial application

Given \(add(x,y) = x + y\), what does \(add(x)\) mean?

  • Definition: \(add(x, y) = add(x)(y)\)
  • Corollary: \(add(x)\) returns a function
  • \(f(x) = add (1)(x)\), so \(f = add(1)\)

"Currying"

Types

2                    :: Integer
3.14                 :: Float
'a'                  :: Char
[0, 1, 2, 3, 4, 5]   :: [Integer]
['D', 'a', 'a', 'n'] :: [Char]

type String = [Char] -- Defines alias
"Hello, Confluente!" :: String

data Degree          -- Defines a new data type
    = Bachelor
    | Master
    | PhD

data Person          -- Defines a new record type
    = Person {
        name :: String,
        age :: Integer,
        degree :: Degree
    }
-- Use like this:
Person {
    name = "Daan de Graaf",
    age = 19,
    degree = Bachelor
}                    :: Person
-- This automatically defines:
name :: Person -> String
age :: Person -> Integer
degree :: Person -> Degree

Types

-- Container that may or may not be empty
data Maybe a
    = Just a
    | Nothing

-- Use like this:
Just 3          :: Maybe Integer
Nothing         :: Maybe Integer

-- Useful when return value is undefined for some input
sqrt :: Float -> Float

sqrt 9          -- 3
sqrt -1         -- ?

-- Better:
sqrt :: Float -> Maybe Float
sqrt 9          -- Just 3
sqrt -1         -- Nothing

Parametric types

Input

Output

Types

type Grade = Float
data Course
    = Course {
        id :: Integer,
        name :: String,
        grade :: Maybe Grade
    }
type Average = Float
transform :: [Course] -> Average
-- We need to implement this function

Transformation

Transform implementation

[Course]
[Maybe Grade]
[Grade]
Float
Integer
Average

Transform implementation

Mapping

-- Easy for a single Course:
grade :: Course -> Maybe Grade

-- But we need:
grades :: [Course] -> [Maybe Grade]

-- For different apps we also need:
ids :: [Course] -> [Integer]
names :: [Course] -> [String]

-- General pattern?
map :: (a -> b) -> [a] -> [b]

-- Substitute Course for a, Maybe Grade for b

map :: (Course -> Maybe Grade) -> [Course] -> [Maybe Grade]

grades :: [Course] -> [Maybe Grade]
grades = map grade

Transform implementation

Hoogle to the rescue

-- We need:
magic :: [Maybe Grade] -> [Grade]

Extracting 'Just' from a list is a common pattern

catMaybes :: [Maybe Grade] -> [Grade]
mapMaybe :: (a -> Maybe b) -> [a] -> [b]
mapMaybe grade :: [Course] -> [Grade]

Transform implementation

Average

sum(X) = \sum_{k=1}^{n} x_k
sum(X)=k=1nxksum(X) = \sum_{k=1}^{n} x_k
X = {x_1, x_2, ..., x_n}
X=x1,x2,...,xnX = {x_1, x_2, ..., x_n}
average(X) = \frac{sum(x_n)}{n}
average(X)=sum(xn)naverage(X) = \frac{sum(x_n)}{n}
type Average = Float
sum :: [Float] -> Float
length :: [a] -> Integer
(/) :: Float -> Float -> Float

average :: [Grade] -> Average
-- This does not work:
average gs = sum gs / length gs
-- Luckily we have:
genericLength :: [a] -> Float

-- This works:
average gs = sum gs / genericLength gs
  • Couldn't match type ‘Integer’ with ‘Float’
  • In the second argument of '(/)', namely 'length gs'

Transform implementation

Putting things together

-- Our end goal
transform :: [Course] -> Average

-- We have:
grade :: Course -> Maybe Grade
mapMaybe :: (a -> Maybe b) -> [a] -> [b]
average :: [Grade] -> Average
transform cs = average . (mapMaybe grade) cs

transform = average . mapMaybe grade
(f \circ g) (x) = f(g(x))
(fg)(x)=f(g(x))(f \circ g) (x) = f(g(x))
transform cs = average (mapMaybe grade cs)

Transform

is

awesome!

How do we get input to transform?

How do we display transformed output?

Getting things done

-- Haskell evaluates this function at startup
main :: IO ()
main = ...
  • Return type IO ()
  • No parameters
  • What is IO a?
    • Command
    • Baton
-- Executing this will yield String
getLine :: IO String

-- Takes a string, yield nothing
putStrLn :: String -> IO ()


-- Example program:
main :: IO ()
main = putStrLn "Hello, world!"

Learning to do

Enter pipeline

  • Pipe creation: IO a
  • Transform
  • Pipe draining: a -> IO ()
  • Use do notation to construct a pipeline:
getLines :: IO [String]
reverse :: [a] -> [a]
putStrLns :: [String] -> IO ()

main :: IO ()
main = do
    lines <- getLines                -- Pipe creation
    let reversed = map reverse lines -- Transformation
    putStrLns reversed               -- Pipe draining

Monad

Learning to do

avg-grade pipeline

getCourses :: IO [Course]
transform :: [Course] -> Average
print :: Average -> IO ()

main :: IO ()
main = do
    courses <- getCourses          -- Pipe creation
    let avg = transform courses    -- Transformation
    print avg                      -- Pipe draining

Functional programming

By wildarch

Functional programming

  • 374