Lecture 1
Fundamentals
FP â Functional Programming
What is function?
In math
Programming is not math!
- Hang: loop indefinitely, take unreasonable time to compute
- Have side-effects: read from files, send requests to web services, etc.
- Throw exceptions: missing file, no internet connection
- Terminate early: insufficient memory
Functions in programming can:
What is FP?
Function is the main building block of programs in FP
- Define
- Call
- Compose
- Pass as an argument
- Inspect
In FP languages it should be easy to do the following with functions
FP Concepts
đš Higher-Order Functions (HOF)
𤸠Algebraic Data Types (ADT)
đŧ Pattern Matching
đ§ Purity
𧹠Immutability
đ Totality
đĻĨ Laziness
Haskell Features
Supports all FP concepts plus:
- Static typing
- Polymorphisms
- Type inference
- Layout-sensitivity
- ML syntax
- Automatic currying
- Garbage collector
- Green threads
- Software Transactional Memory (STM)
- ...
Haskell Toolchain
GHC (Glasgow Haskell Compiler) â the compiler
GHCi (GHC interactive) â interactive interpreter aka REPL (Read-Eval-Print-Loop)
ghcup â Haskell toolchain installer (GHC, cabal, stack, hls)
cabal â a Haskell build tool, the first one
stack â another Haskell build tool based on snapshots
HLSÂ (Haskell Language Server) â provides LSP support for Haskell
GHCi
GHCi is a REPL (Read-Eval-Print-Loop)
Course coding conventions
âšī¸ Terminal (or shell) commands start with $
âšī¸ GHCi commands start with prompt Prelude> or ghci>
GHCi: Arithmetic
Prelude> 3 + 4
7
Prelude> 420 - 397
23
Prelude> 13 * (-3)
-39
Prelude> 2 + 2 * 2
6
Prelude> (2 + 2) * 2
8
Prelude> 2 ^ 16
65536
Prelude> 100 / 8
12.5
Prelude> 0.1 + 0.2
0.30000000000000004
GHCi: Comparison
Prelude> 2 + 2 == 5
False
Prelude> 0.1 + 0.2 /= 0.15 + 0.15
True
Prelude> 6 < 7
True
Prelude> 6 * 7 > 5 * 8
True
Prelude> 10 <= 5
False
Prelude> 100 >= 120
False
GHCi: Logic
Prelude> 1 < 3 && 2 <= 4
True
Prelude> 1 < 3 && 2 >= 4
False
Prelude> 1 < 3 || 2 >= 4
True
Prelude> 1 < 2 && 2 < 3 && 3 < 4
True
Prelude> True || False
True
Prelude> False || True && False
False
GHCi: Calling functions
functionName arg1 arg2 ... argN
Syntax
Examples
Prelude> not True
False
Prelude> div 7 3
2
Prelude> mod 7 3
1
Prelude> max (2 * 3) (3 ^ 2)
9
Prelude> max (min 1 10) (min (-5) 3)
1
() are important!
Prelude> div 7 3 + 2
4
Prelude> div 7 (3 + 2)
1
Prelude> div 7 + 3 2
<interactive>:40:1: error:
âĸ Non type-variable argument in the constraint: Num (a -> a)
(Use FlexibleContexts to permit this)
âĸ When checking the inferred type
it :: forall a t.
(Integral a, Num t, Num (a -> a), Num (t -> a -> a)) =>
a -> a
Types
Expressions have types
Prelude> :t True
True :: Bool
Prelude> :t False
False :: Bool
Prelude> :t 'F'
'F' :: Char
âšī¸ Use :t or :type command to view expression type in GHCi
Prelude> :t Bool
<interactive>:1:1: error: Data constructor not in scope: Bool
Types of numbers
Prelude> :t 21
21 :: Num p => p
Prelude> :t 3.5
3.5 :: Fractional p => p
â ī¸ Numeric constants are polymorphic in Haskell by default
Prelude> :t +d 21
21 :: Integer
Prelude> :t +d 3.5
3.5 :: Double
Types of functions
Prelude> :t not
not :: Bool -> Bool
Prelude> :t div
div :: Integral a => a -> a -> a
Prelude> :t +d div
div :: Integer -> Integer -> Integer
Prelude> :t (&&)
(&&) :: Bool -> Bool -> Bool
Prelude> :t +d (-)
(-) :: Integer -> Integer -> Integer
List
List literals
Prelude> []
[]
Prelude> [3, 1, 2]
[3,1,2]
Prelude> [3 + 4, 5 * 7, 17 - 9]
[7,35,8]
Prelude> [1, 5] ++ [3, 4, 2]
[1,5,3,4,2]
Prelude> 6 : [3, 2, 1]
[6,3,2,1]
Prelude> :t [True, False, True]
[True, False, True] :: [Bool]
Prelude> :t ['a', '1', 'F']
['a', '1', 'F'] :: [Char]
Expessions
Types
List: head/tail/last/init
Prelude> head [3, 1, 2]
3
Prelude> tail [3, 1, 2]
[1,2]
Prelude> last [3, 1, 2]
2
Prelude> init [3, 1, 2]
[3,1]
Watch your head!
Prelude> head []
*** Exception: Prelude.head: empty list
List: more functions
Prelude> reverse [4, 1, 3, 2]
[2,3,1,4]
Prelude> take 2 [4, 3, 7, 10, 9]
[4,3]
Prelude> drop 2 [4, 3, 7, 10, 9]
[7,10,9]
Prelude> null []
True
Prelude> null [3]
False
Prelude> elem 'x' ['a', 'b', 'c']
False
Prelude> concat [[3, 1], [2], [], [5, 10]]
[3,1,2,5,10]
Prelude> length [3, 1, 2]
3
Prelude> [5, 4, 6] !! 0
5
â ī¸ length and !! are slow operations on lists (linear time)
List: ranges
Prelude> [1 .. 10]
[1,2,3,4,5,6,7,8,9,10]
Prelude> [1, 3 .. 20]
[1,3,5,7,9,11,13,15,17,19]
Prelude> [10 .. 1]
[]
Prelude> [10, 9 .. 1]
[10,9,8,7,6,5,4,3,2,1]
Prelude> [0 .. ]
[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,
24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,
45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,
66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,
87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,
106,107,108,109,110,111,112,113,114,115,116...
âž Infinite lists
Lazy evaluation đĻĨ
Prelude> take 3 [0 .. ]
[0,1,2]
Prelude> take 3 (drop 5 [0 .. ])
[5,6,7]
âšī¸ Expressions are evaluated only when needed
Prelude> [5, head [], 7]
[5,*** Exception: Prelude.head: empty list
Prelude> [5, head [], 7] !! 2
7
â ī¸ Be careful with infinite data structures!
Prelude> length [0 .. ]
String
Prelude> :t ['h', 'e', 'l', 'l', 'o']
['h', 'e', 'l', 'l', 'o'] :: [Char]
Prelude> :t "hello"
"hello" :: [Char]
âšī¸ String in Haskell is a list of characters.
Prelude> "The result is: " ++ show [True, False]
"The result is: [True,False]"
Prelude> words "Hello, Haskell world!"
["Hello,","Haskell","world!"]
Prelude> unwords ["My", "name", "is"]
"My name is"
Prelude> take 5 "Hello world!"
"Hello"
Prelude> "Hello, " ++ "Alice!"
"Hello, Alice!"
Syntax
Define a function
functionName :: type
functionName arg1 ... argN = result
Syntax
File : Example.hs Module : Example
module Example where
increase :: Integer -> Integer
increase x = x + 1
*Example> increase 5
6
Loading a module
module Example where
increase :: Integer -> Integer
increase x = x + 1
$ ghci
GHCi, version 8.10.7: https://www.haskell.org/ghc/ :? for help
Prelude> increase 5
<interactive>:1:1: error:
Variable not in scope: increase :: t0 -> t
Prelude> :l Example.hs
[1 of 1] Compiling Example ( Example.hs, interpreted )
Ok, one module loaded.
*Example> increase 5
6
Packages
Prelude> import Data.List
Prelude Data.List> sort [3, 1, 2]
[1,2,3]
Prelude Data.List> nub [True, False, True, True]
[True,False]
module â a collection of functions and custom data types
package â a collection of modules + metadata
Hackage â a central repository of Haskell packages
base â standard Haskell library
Prelude â the module from base imported by default
if-then-else
-- Return first element of the list or the given default value
headOrDefault :: Int -> [Int] -> Int
headOrDefault def list = if null list then def else head list
headOrDefault :: Int -> [Int] -> Int
headOrDefault def list =
if null list
then def
else head list
Prelude> headOrDefault 0 []
0
Prelude> headOrDefault 0 [3, 1, 2]
3
guards
sign :: Int -> String
sign n
| n == 0 = "Zero"
| n < 0 = "Negative"
| otherwise = "Positive"
Prelude> sign 0
"Zero"
Prelude> sign 5
"Positive"
Prelude> sign (-10)
"Negative"
let-in
sameThreeAround :: [Int] -> Bool
sameThreeAround list =
let firstThree = take 3 list
lastThree = reverse (take 3 (reverse list))
in firstThree == lastThree
Prelude> sameThreeAround [1 .. 5]
False
Prelude> sameThreeAround [1,2,3,4,5,1,2,3]
True
let var1 = expr1
var2 = expr2
...
in result
Syntax
let var = expr in result
Example
where
appendLastTwos :: [Int] -> [Int] -> [Int]
appendLastTwos list1 list2 = lastTwo list1 ++ lastTwo list2
where
lastTwo :: [Int] -> [Int]
lastTwo l = reverse (take 2 (reverse l))
Prelude> appendLastTwos [1..5] [1..10]
[4,5,9,10]
Prelude> appendLastTwos [] [3]
[3]
Immutability
Prelude> list = [3, 1, 2, 5, 10]
Prelude> list
[3,1,2,5,10]
Prelude> newList = drop 2 list
Prelude> newList
[2,5,10]
Prelude> list
[3,1,2,5,10]
đ Values are never changed. They can be assigned only once.
How do you change if you can't?
Recursion!
No more loops!
count :: Int -> [Int] -> Int
count n list = go 0 list
where
go :: Int -> [Int] -> Int
go result l =
if null l -- if the list is empty
then result -- then return our accmulated result
else if head l == n
then go (result + 1) (tail l)
else go result (tail l)
Count number of elements equal to given number
Prelude> count 3 [1 .. 5]
1
Prelude> count 5 [1,2,3,5,3,10,5]
2
Higher-Order Functions
First-Class Functions
applyToSame :: (Int -> Int -> Int) -> Int -> Int
applyToSame f x = f x x
âšī¸ You can pass functions as arguments to other functions!
Prelude> applyToSame div 3
1
Prelude> applyToSame mod 3
0
Prelude> applyToSame (+) 3
6
Prelude> applyToSame (*) 3
9
Îģ Lambda functions ÎģÂ
satisfies :: (Int -> Bool) -> Int -> String
satisfies check n
| check n = "The number " ++ show n ++ " passes check"
| otherwise = "The number " ++ show n ++ " doesn't pass"
\var1 var2 ... varN -> expression
Syntax
Example
Prelude> satisfies (\x -> x > 0) 5
"The number 5 passes check"
Prelude> satisfies (\x -> x > 0) (-3)
"The number -3 doesn't pass"
Partial application
applyTwice :: (Integer -> Integer) -> Integer -> Integer
applyTwice f x = f (f x)
Prelude> div12By = div 12
Prelude> :t +d div12By
div12By :: Integer -> Integer
Prelude> div12By 3
4
Prelude> :t (True ||)
(True ||) :: Bool -> Bool
Prelude> :t +d (* 3)
(* 3) :: Integer -> Integer
Prelude> applyTwice (+ 20) 17
57
Prelude> applyTwice (* 3) 4
36
Standard HOFs
Prelude> map not [True, False, True, True]
[False,True,False,False]
Prelude> map (* 2) [1 .. 5]
[2,4,6,8,10]
Prelude> filter even [3, 1, 2, 5, 4, 0, 10, 7]
[2,4,0,10]
Prelude> filter (<3) [10, 9 .. 0]
[2,1,0]
Prelude> any (> 10) [7, 5, 9, 3]
False
Prelude> concatMap (replicate 3) [1 .. 5]
[1,1,1,2,2,2,3,3,3,4,4,4,5,5,5]
Prelude> take 10 (iterate (* 2) 1)
[1,2,4,8,16,32,64,128,256,512]
Functions inside lists
Prelude> head [(* 2), div 12, (+ 10)] 5
10
Prelude> last [(* 2), div 12, (+ 10)] 5
15
âšī¸ You can also store functions inside lists!
đŠâđŦ Functions are automatically curried. No need to use special syntax for calling functions, just space is enough đž
Recap: GHCi commands
:q â quit GHCi
:t â the type of an expression
:t +d â the type of an expression with defaulting
:l â load a module
:i â information about a function or type
More sources
Lecture 1: Fundamentals
By Haskell Beginners 2022
Lecture 1: Fundamentals
Fundamentals of Functional Programming and Haskell
- 8,409