Functional Programming - FP101
- UNIFEI - October - 2018
Hello!
Prof. Edmilson
Hanneli Tavante
<3
Prolog
Prof. Maurilio
<3
Deutschland
Questions
- What is this course?
- A: An attempt to bring interesting subjects to the courses at UNIFEI
- Why are the slides in English?
- I am reusing some material that I prepared before
- What should I expect?
- This is the first time we give this course. We will be looking forward to hearing your feedback
Rules
- Don't skip classes
- The final assignment is optional
- We will have OPTIONAL assignments during the classes
- Feel free to ask questions
- Don't feel discouraged with the mathematics
- Contact us for study group options
[GIF time]
Agenda
- Foundation concepts; intro to Programming Languages
- Why Functional Programming (FP)?
- Types
- Lists (gotta love them)
- Recursion
- Higher order functions (Hof)
- Monads
- Lazy Evaluation
- A word or two about Lambda Calculus
- Final considerations
Note: this list can change
WAT
Agenda
- Foundation concepts; intro to Programming Languages
- Why Functional Programming (FP)?
- Types and classes
- Lists (gotta love them)
- Recursion
- Higher order functions (Hof)
- Monads
- Lazy Evaluation
- A word or two about Lambda Calculus
- Final considerations
Note: this list can change
Question: Given a list of numbers
[1, 2, 3, 4, 5]
Obtain another list with the same elements multiplied by 2
Can you implement a solution for this? (in any language)
Is this the only way to solve this?
No.
[1, 2, 3, 4, 5].map { |el| el*2 }
=> [2, 4, 6, 8, 10]
Which language is this??
A: Ruby!
[ el*2 for el in [1, 2, 3, 4, 5]]
[2, 4, 6, 8, 10]
Which language is this??
A: Python!
[ el*2 | el <- [1..5] ]
[2, 4, 6, 8, 10]
Which language is this??
A: Haskell!
List(1, 2, 3, 4, 5).map( el => el*2)
res0: List[Int] = List(2, 4, 6, 8, 10)
Which language is this??
A: Scala!
fn main() {
let res = vec![1, 2, 3, 4, 5].iter()
.map(|&el| el*2).collect::<Vec<_>>();
println!("{:?}", res);
}
[2, 4, 6, 8, 10]
Which language is this??
A: Rust!
There are many languages out there!
There are different styles to write code (paradigm)
What some of you might have seen in classes is the imperative style
But there are other styles!
There is one style of programming in which expressions are more important than statements
Note: examples of statements: for, do/while, {...}, return
Agenda
- Foundation concepts; intro to Programming Languages
- Why Functional Programming (FP)?
- Types
- Lists (gotta love them)
- Recursion
- Higher order functions (Hof)
- Monads
- Lazy Evaluation
- A word or two about Lambda Calculus
- Final considerations
Challenge: Create a program to sum the numbers from 0 to 100 (including 100)
//pseudocode
int res = 0;
for (int i=0; i<= 100; i++) {
res = res + i;
}
What are the problems of this code?
Is there anything you can't predict?
There are two things to be aware:
res: 5050
1. Variable assignments / Statements
2. Mutable states
Is it possible to write the same program only using expressions?
sum[1..100]
5050
(This is Haskell)
sum[1..100]
There are two expressions here:
sum()
1..100
Some historical background
Turing
Alonzo Church
"How can we formalize the concept of effective computability?"
Turing Machines
Lambda Calculus
Like the calculus you see at MAT001, λ-calculus is a formalism
J. McCarthy
Lisp Programming Language - one of the first functional languages
Functional Programming => One way to implement λ-calculus formalisms
Other languages: ML, Haskell
Why is this programming style so important?
λ-calculus
Powered by Mathematics!
How do you choose a programming language?
Agenda
- Foundation concepts; intro to Programming Languages
- Why Functional Programming (FP)?
- Types
- Lists (gotta love them)
- Recursion
- Higher order functions (Hof)
- Monads
- Lazy Evaluation
- A word or two about Lambda Calculus
- Final considerations
In all languages, even in Assembly, we have at least two components:
Data
Operations
Not all of the available operations make sense to all kinds of data.
If you use incompatible pieces of data for an operation, you will have a representation error
Programming languages use a type system to look at a program and determine if a representation error will happen or not
Wait - what is a type?
Type: a name for a collection of related values
{ True, False }: Bool
Wait - what is a type system?
Let's ask Wikipedia:
"In programming languages, a type system is a collection of rules that assign a property called type to various constructs a computer program consists of, such as variables, expressions, functions or modules"
Type System => Collection of rules to manage collections of related values
What are the possible strategies that a type system can use to handle representation errors?
Strategies
- Generate a compile error
- Perform a type check before run the code
- Well defined error set
- Unpredictable runtime errors
- Try implicit conversion
- A compiler tags pieces of code and tries to infer if the behaviour will be valid or not (before the program runs)
- A compiler / interpreter generates code to keep track of the data
Strategies
- Generate a compile error
- Perform a type check before run the code
- Well defined error set
- Unpredictable runtime errors
- Try implicit conversion
- A compiler tags pieces of code and tries to infer if the behaviour will be valid or not (before the program runs)
- A compiler / interpreter generates code to keep track of the data
"Strong"
"Weak"
"Static"
"Dynamic"
* Definitions are not exact on literature
You don't have to choose only one alternative
Java: static (why?)
Python: dynamic
Can you see the benefits of statically typed languages?
- A compiler tags pieces of code and tries to infer if the behaviour will be valid or not (before the program runs)
"Static"
Benefits of statically typed languages
1. Reduce the number of runtime errors
Benefits of statically typed languages
2. The compiler can calculate the type of an expression
Type inference
Benefits of statically typed languages
3. The type the compiler calculates corresponds to the type you obtain when you execute the expression in runtime
Type Soundness
Each language may have some basic types
Basic Types
- Lists
(group of elements, the types can be different)
- Tuples
(Sequence of values where all the elements have the same type)
expr::type
This is the terminology we use to indicate that an expression has a certain type (::)
In Haskell,
Prelude> :type 1
1 :: Num t => t
Functions also have types
Can you put 'Function' and 'type' in the same sentence so that you can come up with a definition of function?
"Function is a mapping of values from one type to another type"
An exercise
Let's write a function to sum two integers in Haskell
Attempt #1
addd (x, y) = x+y
addd (3, 4)
7
Attempt #2
addd x y = x+y
addd 3 4
7
What is the difference?
What is the difference?
Attempt #1
addd (x, y) = x+y
addd (3, 4)
7
Attempt #2
addd x y = x+y
addd 3 4
7
:t addd
addd :: Num a => (a, a) -> a
:t addd
addd :: Num a => a -> a -> a
Attempt #2
addd x y = x+y
addd 3 4
7
:t addd
addd :: Num a => a -> a -> a
There is an advantage in writing the function like this:
It is a function (1)
That returns a function (2)
That takes another number (3)
And returns a number (4)
Attempt #2
addd x y = x+y
addd 3 4
7
:t addd
addd :: Num a => a -> a -> a
There is an advantage in writing the function like this:
:t addd
addd :: Num a => a -> (a -> a)
Is equivalent to:
Remember: Arrows associate to the right (*)
Attempt #2
addd x y = x+y
addd 3 4
7
:t addd
addd :: Num a => a -> (a -> a)
Now it is easier to see:
It is a function (1)
That returns a function (2)
That takes another number (3)
And returns a number (4)
Every time our functions take their arguments one by one, we have
a Curried Function
:t addd
addd :: Num a => a -> a -> a
Remember: Arrows associate to the right
(*)
:t addd
addd :: Num a => a -> (a -> a)
Remember: Function applications associates to the left
mult x y z
((mult x) y) z
Question: Does addd work for float numbers?
:t addd
addd :: Num a => a -> (a -> a)
addd 3.1 3.2
6.300000000000001
How can we restrict that to Integers only?
addd :: Int -> (Int -> Int)
addd x y = x+y
:t addd
addd :: Int -> Int -> Int
addd 3.1 3.2
error: No instance for
(Fractional Int)
arising from the literal ‘3.1’
A few sentences about why this is useful
Agenda
- Foundation concepts; intro to Programming Languages
- Why Functional Programming (FP)?
- Types
- Lists (gotta love them)
- Recursion
- Higher order functions (Hof)
- Monads
- Lazy Evaluation
- A word or two about Lambda Calculus
- Final considerations
Note: This lecture would be too boring if I showed you 1h of code. We will alternate the contents
Lists
[1, 2, 3, 4, 5]
What are lists in terms of expressions?
Whiteboard time! Let's understand the x:xs notation
What are lists in terms of expressions?
1:(2:(3:(4:(5:[ ]))))
How do we manipulate lists?
(ex: transform elements into other elements, get only specific elements, transform lists into tuples)
A: List Comprehensions
List comprehensions
[x^2 | x <- [1..5]]
generator:
defines how to generate the values of x
function to
transform x
Filters
[x | x <- [1..5], even x]
Get the number, no transformation
Get only the even numbers of a list
generator
filter
Challenge: Determine the position(s) of an element in a list
positions 2 [1, 2, 3, 4, 5, 2, 1, 3]
A: 1 and 5
Problem: Lists do not have an index!
Start rom the beginning
positions
function name
x
xs
el
list
=
Strategy: transform each element of the list in a tuple: (el, pos)
The solution
[1, 2, 3, 4, 5, 2, 1, 3]
Our list
[0, 1, 2, 3, 4, 5, 6, 7]
Positions
Solution
(1, 0)
,(2, 1)
,(3, 2)
[
...
zip
operation
The code
[0, 1, 2, 3, 4, 5, 6, 7]
Positions
Goal
[(1, 0), (2, 1) ...]
zip
operation
positions x xs =
zip xs [1, 2, 3, 4, 5, 2, 1, 3]
The code
[0, 1, 2, 3, 4, 5, 6, 7]
Positions
Goal
[(1, 0), (2, 1) ...]
zip operation
positions x xs =
zip xs [1..n]
where n = length xs - 1
The code
[0, 1, 2, 3, 4, 5, 6, 7]
Positions
Goal
[(1, 0), (2, 1) ...]
zip operation
positions x xs =
zip xs [1..n]
where n = length xs - 1
<-
( , )
el, pos
get pos
i |
The code
[0, 1, 2, 3, 4, 5, 6, 7]
Positions
Goal
[(1, 0), (2, 1) ...]
zip operation
positions x xs =
zip xs [1..n]
where n = length xs - 1
<-
( , i)
el, pos
list comprehension
i |
[
Now we need to filter the elements that we specified
The code
[0, 1, 2, 3, 4, 5, 6, 7]
Positions
Goal
[(1, 0), (2, 1) ...]
zip operation
positions x xs =
zip xs [1..n]
where n = length xs - 1
<-
( , i)
el, pos
list comprehension
i |
[
,
x'== x
(x', i)
]
Break
What are functions?
Remember, Haskell has a strong link to mathematics
In λ-calculus, we have λ expressions
expression that denotes functions
λ:
Let's give it a try
Write the addd function as a λ expression
addd x y = x+y
Agenda
- Foundation concepts; intro to Programming Languages
- Why Functional Programming (FP)?
- Types
- Lists
- Recursion
- Higher order functions (Hof)
- Monads
- Lazy Evaluation
- A word or two about Lambda Calculus
- Final considerations
Why is recursion such a nice feature?
One reason is being able to prove properties by mathematical induction :)
The practical reason here is that it helps us to get rid of statements
Challenge - write a length function using recursion
1. Let's define the types
length ::
[a]
-> Int
Challenge - write a length function using recursion
1. Let's define the types
length ::
[a]
-> Int
2. Define the base case
The "base case" is the one that will not change the result
length [] = 0
Challenge - write a length function using recursion
1. Let's define the types
length ::
[a]
-> Int
2. Define the base case
length [] = 0
3. Define the recursive structure
Add one to each element of the list, until you get an empty list
Remember: this is a list
1:(2:(3:(4:(5:[ ]))))
Challenge - write a length function using recursion
1. Let's define the types
length ::
[a]
-> Int
2. Define the base case
length [] = 0
3. Define the recursive structure
length ( : xs) = 1 + length xs
Anything
(_ : xs)
When we create recursive functions, we must figure out a good way to abstract the patterns of the function
We may be able to abstract these patterns with functions of functions, or a.k.a.
Agenda
- Foundation concepts; intro to Programming Languages
- Why Functional Programming (FP)?
- Types
- Lists
- Recursion
- Higher order functions (Hof)
- Monads
- Lazy Evaluation
- A word or two about Lambda Calculus
- Final considerations
~switching presentations~
Higher order functions are essential to abstractions
Whiteboard: foldr example
Agenda
- Foundation concepts; intro to Programming Languages
- Why Functional Programming (FP)?
- Types
- Lists
- Recursion
- Higher order functions (Hof)
- Monads
- Lazy Evaluation
- A word or two about Lambda Calculus
- Final considerations
Whiteboard time
Use this video for reference (Computerphile)
IO/Monad (MOAR whiteboard)
There is an important question none asked:
How are expressions evaluated?
Agenda
- Foundation concepts; intro to Programming Languages
- Why Functional Programming (FP)?
- Types
- Lists
- Recursion
- Higher order functions (Hof)
- Monads
- Lazy Evaluation
- A word or two about Lambda Calculus
- Final considerations
Haskell only evaluates the minimum necessary
Lazy Evaluation
Agenda
- Foundation concepts; intro to Programming Languages
- Why Functional Programming (FP)?
- Types
- Lists
- Recursion
- Higher order functions (Hof)
- Monads
- Lazy Evaluation
- A word or two about Lambda Calculus
- Final considerations
Exercise - Section 5 of this paper +
Church Numerals
References
Functional Programming - FP101
By Hanneli Tavante (hannelita)
Functional Programming - FP101
- 1,661