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

 

  1. Foundation concepts; intro to Programming Languages
  2. Why Functional Programming (FP)?
  3. Types
  4. Lists (gotta love them)
  5. Recursion
  6. Higher order functions (Hof)
  7. Monads
  8. Lazy Evaluation
  9. A word or two about Lambda Calculus
  10. Final considerations

Note: this list can change

WAT

Agenda

 

  1. Foundation concepts; intro to Programming Languages
  2. Why Functional Programming (FP)?
  3. Types and classes
  4. Lists (gotta love them)
  5. Recursion
  6. Higher order functions (Hof)
  7. Monads
  8. Lazy Evaluation
  9. A word or two about Lambda Calculus
  10. 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

 

  1. Foundation concepts; intro to Programming Languages
  2. Why Functional Programming (FP)?
  3. Types
  4. Lists (gotta love them)
  5. Recursion
  6. Higher order functions (Hof)
  7. Monads
  8. Lazy Evaluation
  9. A word or two about Lambda Calculus
  10. 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

 

  1. Foundation concepts; intro to Programming Languages
  2. Why Functional Programming (FP)?
  3. Types
  4. Lists (gotta love them)
  5. Recursion
  6. Higher order functions (Hof)
  7. Monads
  8. Lazy Evaluation
  9. A word or two about Lambda Calculus
  10. 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

 

  1. Foundation concepts; intro to Programming Languages
  2. Why Functional Programming (FP)?
  3. Types
  4.  Lists (gotta love them)
  5.  Recursion
  6. Higher order functions (Hof)
  7. Monads
  8. Lazy Evaluation
  9. A word or two about Lambda Calculus
  10. 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

\lambda x -> x+x
λx&gt;x+x\lambda x -&gt; x+x

expression that denotes functions

λ:

Let's give it a try

Write the addd function as a  λ expression

addd x y = x+y
add = \lambda x \rightarrow ( \lambda y \rightarrow x+y )
add=λx(λyx+y)add = \lambda x \rightarrow ( \lambda y \rightarrow x+y )

Agenda

 

  1. Foundation concepts; intro to Programming Languages
  2. Why Functional Programming (FP)?
  3. Types
  4.  Lists
  5.  Recursion
  6. Higher order functions (Hof)
  7. Monads
  8. Lazy Evaluation
  9. A word or two about Lambda Calculus
  10. 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

 

  1. Foundation concepts; intro to Programming Languages
  2. Why Functional Programming (FP)?
  3. Types
  4.  Lists
  5.  Recursion
  6. Higher order functions (Hof)
  7. Monads
  8. Lazy Evaluation
  9. A word or two about Lambda Calculus
  10. Final considerations

~switching presentations~

Higher order functions are essential to abstractions

Whiteboard: foldr example

Agenda

 

  1. Foundation concepts; intro to Programming Languages
  2. Why Functional Programming (FP)?
  3. Types
  4.  Lists
  5.  Recursion
  6. Higher order functions (Hof)
  7. Monads
  8. Lazy Evaluation
  9. A word or two about Lambda Calculus
  10. 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

 

  1. Foundation concepts; intro to Programming Languages
  2. Why Functional Programming (FP)?
  3. Types
  4.  Lists
  5.  Recursion
  6. Higher order functions (Hof)
  7. Monads
  8. Lazy Evaluation
  9. A word or two about Lambda Calculus
  10. Final considerations

Haskell only evaluates the minimum necessary

Lazy Evaluation

Agenda

 

  1. Foundation concepts; intro to Programming Languages
  2. Why Functional Programming (FP)?
  3. Types
  4.  Lists
  5.  Recursion
  6. Higher order functions (Hof)
  7. Monads
  8. Lazy Evaluation
  9. A word or two about Lambda Calculus
  10. Final considerations

Exercise - Section 5 of this paper +

Church Numerals

References