The Functional Paradigm MinHS
Functional Programming Languages
Dr. Liam O’Connor
University of Edinburgh LFCS UNSW, Term 3 2020
1
The Functional Paradigm
MinHS
Functional Programming
Many languages have been called functional over the years:
Lisp
[(= (length lst) 1) (first lst)]
[else (max (first lst) (max-of (rest lst)))]))
(define (max-of lst) (cond
2
The Functional Paradigm
MinHS
Functional Programming
Many languages have been called functional over the years:
Haskell
maxOf :: [Int] → Int maxOf = foldr1 max
Lisp
[(= (length lst) 1) (first lst)]
[else (max (first lst) (max-of (rest lst)))]))
(define (max-of lst) (cond
3
The Functional Paradigm
MinHS
Functional Programming
Many languages have been called functional over the years:
Haskell
maxOf :: [Int] → Int maxOf = foldr1 max
Lisp
[(= (length lst) 1) (first lst)]
[else (max (first lst) (max-of (rest lst)))]))
(define (max-of lst) (cond
4
JavaScript?
function maxOf(arr) {
var max = arr .reduce(function(a, b) {
return Math.max(a, b); }); }
The Functional Paradigm
MinHS
Functional Programming
Many languages have been called functional over the years:
Haskell
maxOf :: [Int] → Int maxOf = foldr1 max
Lisp
[(= (length lst) 1) (first lst)]
[else (max (first lst) (max-of (rest lst)))]))
(define (max-of lst) (cond
What do they have in common?
5
JavaScript?
function maxOf(arr) {
var max = arr .reduce(function(a, b) {
return Math.max(a, b); }); }
The Functional Paradigm
MinHS
Definitions
Unlike imperative languages, functional programming languages are not very crisply defined.
Attempt at a Definition
A functional programming language is a programming language derived from or inspired by the λ-calculus, or derived from or inspired by another functional programming language.
The result? If it has λ in it, you can call it functional.
6
The Functional Paradigm
MinHS
Definitions
Unlike imperative languages, functional programming languages are not very crisply defined.
Attempt at a Definition
A functional programming language is a programming language derived from or inspired by the λ-calculus, or derived from or inspired by another functional programming language.
The result? If it has λ in it, you can call it functional.
In this course, we’ll consider purely functional languages, which have a much better definition.
7
The Functional Paradigm
MinHS
Why Study FP Languages?
Think of a major innovation in the area of programming languages.
Garbage Collection?
The Functional Paradigm
MinHS
Why Study FP Languages?
Think of a major innovation in the area of programming languages.
Garbage Collection?
Lisp, 1958
The Functional Paradigm
MinHS
Why Study FP Languages?
Think of a major innovation in the area of programming languages.
Functions as Values?
Garbage Collection?
Lisp, 1958
The Functional Paradigm
MinHS
Why Study FP Languages?
Think of a major innovation in the area of programming languages.
Functions as Values?
Lisp, 1958
Garbage Collection?
Lisp, 1958
The Functional Paradigm
MinHS
Why Study FP Languages?
Think of a major innovation in the area of programming languages.
Polymorphism?
Functions as Values?
Lisp, 1958
Garbage Collection?
Lisp, 1958
The Functional Paradigm
MinHS
Why Study FP Languages?
Think of a major innovation in the area of programming languages.
Polymorphism?
ML, 1973
Functions as Values?
Lisp, 1958
Garbage Collection?
Lisp, 1958
The Functional Paradigm
MinHS
Why Study FP Languages?
Think of a major innovation in the area of programming languages.
Polymorphism?
ML, 1973
Functions as Values?
Lisp, 1958
Garbage Collection?
Lisp, 1958
Type Inference?
The Functional Paradigm
MinHS
Why Study FP Languages?
Think of a major innovation in the area of programming languages.
Polymorphism?
ML, 1973
Functions as Values?
Lisp, 1958
Garbage Collection?
Lisp, 1958
Type Inference?
ML, 1973
The Functional Paradigm
MinHS
Why Study FP Languages?
Think of a major innovation in the area of programming languages.
Polymorphism?
ML, 1973
Functions as Values?
Lisp, 1958
Garbage Collection?
Lisp, 1958
Type Inference?
ML, 1973
Metaprogramming?
The Functional Paradigm
MinHS
Why Study FP Languages?
Think of a major innovation in the area of programming languages.
Polymorphism?
ML, 1973
Functions as Values?
Lisp, 1958
Garbage Collection?
Lisp, 1958
Type Inference?
ML, 1973
Metaprogramming?
Lisp, 1958
The Functional Paradigm
MinHS
Why Study FP Languages?
Think of a major innovation in the area of programming languages.
Polymorphism?
ML, 1973
Functions as Values?
Lisp, 1958
Lazy Evaluation?
Garbage Collection?
Lisp, 1958
Type Inference?
ML, 1973
Metaprogramming?
Lisp, 1958
The Functional Paradigm
MinHS
Why Study FP Languages?
Think of a major innovation in the area of programming languages.
Polymorphism?
ML, 1973
Functions as Values?
Lisp, 1958
Lazy Evaluation?
Miranda, 1985
Garbage Collection?
Lisp, 1958
Type Inference?
ML, 1973
Metaprogramming?
Lisp, 1958
The Functional Paradigm
MinHS
Why Study FP Languages?
Think of a major innovation in the area of programming languages.
Monads?
Type Inference?
ML, 1973
Polymorphism?
ML, 1973
Functions as Values?
Lisp, 1958
Lazy Evaluation?
Miranda, 1985
Garbage Collection?
Lisp, 1958
Metaprogramming?
Lisp, 1958
The Functional Paradigm
MinHS
Why Study FP Languages?
Think of a major innovation in the area of programming languages.
Monads?
Haskell, 1991
Type Inference?
ML, 1973
Polymorphism?
ML, 1973
Functions as Values?
Lisp, 1958
Lazy Evaluation?
Miranda, 1985
Garbage Collection?
Lisp, 1958
Metaprogramming?
Lisp, 1958
The Functional Paradigm
MinHS
Why Study FP Languages?
Think of a major innovation in the area of programming languages.
Monads?
Haskell, 1991
Type Inference?
ML, 1973
Software Transactional Memory?
Polymorphism?
ML, 1973
Functions as Values?
Lisp, 1958
Lisp, 1958
Garbage Collection?
Metaprogramming?
Lisp, 1958
Lazy Evaluation?
Miranda, 1985
The Functional Paradigm
MinHS
Why Study FP Languages?
Think of a major innovation in the area of programming languages.
Monads?
Haskell, 1991
Type Inference?
ML, 1973
Software Transactional Memory?
GHC Haskell, 2005
Polymorphism?
ML, 1973
Functions as Values?
Lisp, 1958
Lisp, 1958
Garbage Collection?
Metaprogramming?
Lisp, 1958
Lazy Evaluation?
Miranda, 1985
23
The Functional Paradigm
MinHS
Purely Functional Programming Languages
The term purely functional has a very crisp definition. Definition
A programming language is purely functional if β-reduction (or evaluation in general) is actually a confluence.
In other words, functions have to be mathematical functions, and free of side effects.
24
The Functional Paradigm
MinHS
Purely Functional Programming Languages
The term purely functional has a very crisp definition. Definition
A programming language is purely functional if β-reduction (or evaluation in general) is actually a confluence.
In other words, functions have to be mathematical functions, and free of side effects.
Consider what would happen if we allowed effects in a functional language:
count = 0;
f x = {count := count + x; return count}; m=(λy.y+y)(f 3)
If we evaluate f 3 first, we will get m = 6, but if we β-reduce m first, we will get m = 9. ⇒ not confluent.
25
The Functional Paradigm
MinHS
Making a Functional Language
We’re going to make a language called MinHS.
1 Three types of values: integers, booleans, and functions.
26
The Functional Paradigm
MinHS
Making a Functional Language
We’re going to make a language called MinHS.
1 Three types of values: integers, booleans, and functions.
2 Static type checking (not inference)
27
The Functional Paradigm
MinHS
Making a Functional Language
We’re going to make a language called MinHS.
1 Three types of values: integers, booleans, and functions.
2 Static type checking (not inference)
3 Purely functional (no effects)
28
The Functional Paradigm
MinHS
Making a Functional Language
We’re going to make a language called MinHS.
1 Three types of values: integers, booleans, and functions.
2 Static type checking (not inference)
3 Purely functional (no effects)
4 Call-by-value (strict evaluation)
29
The Functional Paradigm
MinHS
Making a Functional Language
We’re going to make a language called MinHS.
1 Three types of values: integers, booleans, and functions.
2 Static type checking (not inference)
3 Purely functional (no effects)
4 Call-by-value (strict evaluation)
In your Assignment 1, you will be implementing an evaluator for a slightly less minimal dialect of MinHS.
30
The Functional Paradigm
MinHS
Integers
Identifiers
Literals
Types
InfixOperators Expressions e ::=
Syntax
n ::= ···
f,x ::= ···
b ::= True | False
τ ::= Bool|Int|τ1 →τ2
::= *|+|==| ··· x|n|b|(e)|e1e2
| if e1 then e2 else e3
31
The Functional Paradigm
MinHS
Integers
Identifiers
Literals
Types
InfixOperators Expressions e ::=
Syntax
n ::= ···
f,x ::= ···
b ::= True | False
τ ::= Bool|Int|τ1 →τ2
::= *|+|==| ··· x|n|b|(e)|e1e2
| if e1 then e2 else e3
| e1e2
32
The Functional Paradigm
MinHS
Integers
Identifiers
Literals
Types
InfixOperators Expressions e ::=
Syntax
n ::= ···
f,x ::= ···
b ::= True | False
τ ::= Bool|Int|τ1 →τ2
::= *|+|==| ··· x|n|b|(e)|e1e2
| if e1 then e2 else e3
| e1e2
| recfunf::(τ1→τ2)x=e
↑ Like λ, but with recursion.
As usual, this is ambiguous concrete syntax. But all the precedence and associativity rule apply as in Haskell. We assume a suitable parser.
33
The Functional Paradigm
MinHS
Examples
Example (Stupid division by 5)
recfun divBy5 :: (Int → Int) x = if x < 5
then 0
else 1 + divBy5 (x − 5)
Example (Average Function)
recfun average :: (Int → (Int → Int)) x = recfun avX :: (Int → Int) y =
(x + y) / 2
As in Haskell, (average 15 5) = ((average 15) 5).
34
The Functional Paradigm
MinHS
We don’t need no let
This language is so minimal, it doesn’t even need let expressions. How can we do without them?
35
The Functional Paradigm
MinHS
We don’t need no let
This language is so minimal, it doesn’t even need let expressions. How can we do without them?
letx::τ1=e1ine2::τ2 ≡ (recfunf::(τ1→τ2)x=e2)e1
36
The Functional Paradigm
MinHS
Abstract Syntax
Moving to first order abstract syntax, we get:
1 Things like numbers and boolean literals are wrapped in terms (Num, Lit)
2 Operators like a + b become (Plus a b).
37
The Functional Paradigm
MinHS
Abstract Syntax
Moving to first order abstract syntax, we get:
1 Things like numbers and boolean literals are wrapped in terms (Num, Lit)
2 Operators like a + b become (Plus a b).
3 ifc thent elsee becomes(Ifc t e).
38
The Functional Paradigm
MinHS
Abstract Syntax
Moving to first order abstract syntax, we get:
1 Things like numbers and boolean literals are wrapped in terms (Num, Lit)
2 Operators like a + b become (Plus a b).
3 ifc thent elsee becomes(Ifc t e).
4 Function applications e1 e2 become explicit (Apply e1 e2).
39
The Functional Paradigm
MinHS
Abstract Syntax
Moving to first order abstract syntax, we get:
1 Things like numbers and boolean literals are wrapped in terms (Num, Lit)
2 Operators like a + b become (Plus a b).
3 ifc thent elsee becomes(Ifc t e).
4 Function applications e1 e2 become explicit (Apply e1 e2).
5 recfunf::(τ1→τ2)x=ebecomes(Recfunτ1τ2fxe).
40
The Functional Paradigm
MinHS
Abstract Syntax
Moving to first order abstract syntax, we get:
1 Things like numbers and boolean literals are wrapped in terms (Num, Lit)
2 Operators like a + b become (Plus a b).
3 ifc thent elsee becomes(Ifc t e).
4 Function applications e1 e2 become explicit (Apply e1 e2).
5 recfunf::(τ1→τ2)x=ebecomes(Recfunτ1τ2fxe).
6 Variable usages are wrapped in a term (Var x).
41
The Functional Paradigm
MinHS
Abstract Syntax
Moving to first order abstract syntax, we get:
1 Things like numbers and boolean literals are wrapped in terms (Num, Lit)
2 Operators like a + b become (Plus a b).
3 ifc thent elsee becomes(Ifc t e).
4 Function applications e1 e2 become explicit (Apply e1 e2).
5 recfunf::(τ1→τ2)x=ebecomes(Recfunτ1τ2fxe).
6 Variable usages are wrapped in a term (Var x).
What changes when we move to higher order abstract syntax?
42
The Functional Paradigm
MinHS
Abstract Syntax
Moving to first order abstract syntax, we get:
1 Things like numbers and boolean literals are wrapped in terms (Num, Lit)
2 Operators like a + b become (Plus a b).
3 ifc thent elsee becomes(Ifc t e).
4 Function applications e1 e2 become explicit (Apply e1 e2).
5 recfunf::(τ1→τ2)x=ebecomes(Recfunτ1τ2fxe).
6 Variable usages are wrapped in a term (Var x).
What changes when we move to higher order abstract syntax?
1 Var terms go away – we use the meta-language’s variables.
2 (Recfun τ1 τ2 f x e) now uses meta-language abstraction: (Recfun τ1 τ2 (f . x. e)).
43
The Functional Paradigm
MinHS
Working Statically with HOAS
To Code
We’re going to write code for an AST and pretty-printer for MinHS with HOAS. Seeing as this requires us to look under abstractions without evaluating the term, we have to extend the AST with special “tag” values.
44
The Functional Paradigm
MinHS
Static Semantics
To check if a MinHS program is well-formed, we need to check:
1 Scoping – all variables used must be well defined
2 Typing – all operations must be used on compatible types.
45
The Functional Paradigm
MinHS
Static Semantics
To check if a MinHS program is well-formed, we need to check:
1 Scoping – all variables used must be well defined
2 Typing – all operations must be used on compatible types.
Our judgement is an extension of the scoping rules to include types:
Γ⊢e:τ
The context Γ includes typing assumptions for the variables:
x : Int,y : Int ⊢ (Plus x y) : Int
Under this context of assumptions
The expression is assigned this type
46
The Functional Paradigm
MinHS
Static Semantics
Γ⊢(Numn):Int Γ⊢(Litb):Bool Γ⊢e1 :Int Γ⊢e2 :Int Γ⊢(Pluse1 e2):Int
Γ⊢(Ife1 e2 e3):
Let’s implement a type checker.
The Functional Paradigm
MinHS
Static Semantics
Γ⊢(Numn):Int Γ⊢(Litb):Bool Γ⊢e1 :Int Γ⊢e2 :Int Γ⊢(Pluse1 e2):Int
Γ⊢(Ife1 e2 e3):
Let’s implement a type checker.
48
The Functional Paradigm
MinHS
Static Semantics
Γ⊢(Numn):Int Γ⊢(Litb):Bool Γ⊢e1 :Int Γ⊢e2 :Int Γ⊢(Pluse1 e2):Int
Γ⊢e1 :Bool
Γ⊢(Ife1 e2 e3):
Let’s implement a type checker.
49
The Functional Paradigm
MinHS
Static Semantics
Γ⊢(Numn):Int Γ⊢(Litb):Bool Γ⊢e1 :Int Γ⊢e2 :Int Γ⊢(Pluse1 e2):Int
Γ⊢e1 :Bool Γ⊢e2 :τ Γ⊢e3 :τ Γ⊢(Ife1 e2 e3):τ
Let’s implement a type checker.
50
The Functional Paradigm
MinHS
Static Semantics
Γ⊢(Numn):Int Γ⊢(Litb):Bool Γ⊢e1 :Int Γ⊢e2 :Int Γ⊢(Pluse1 e2):Int
Γ⊢e1 :Bool Γ⊢e2 :τ Γ⊢e3 :τ Γ⊢(Ife1 e2 e3):τ
(x : τ) ∈ Γ Γ⊢x:τ
Let’s implement a type checker.
51
The Functional Paradigm
MinHS
Static Semantics
Γ⊢(Numn):Int Γ⊢(Litb):Bool Γ⊢e1 :Int Γ⊢e2 :Int Γ⊢(Pluse1 e2):Int
Γ⊢e1 :Bool Γ⊢e2 :τ Γ⊢e3 :τ Γ⊢(Ife1 e2 e3):τ
(x : τ) ∈ Γ Γ, ⊢ e : τ2 Γ⊢x:τ Γ⊢(Recfunτ1 τ2 (f.x.e)):
Let’s implement a type checker.
52
The Functional Paradigm
MinHS
Static Semantics
Γ⊢(Numn):Int Γ⊢(Litb):Bool Γ⊢e1 :Int Γ⊢e2 :Int Γ⊢(Pluse1 e2):Int
Γ⊢e1 :Bool Γ⊢e2 :τ Γ⊢e3 :τ Γ⊢(Ife1 e2 e3):τ
(x : τ) ∈ Γ Γ,x : τ1, ⊢ e : τ2 Γ⊢x:τ Γ⊢(Recfunτ1 τ2 (f.x.e)):
Let’s implement a type checker.
53
The Functional Paradigm
MinHS
Static Semantics
Γ⊢(Numn):Int Γ⊢(Litb):Bool Γ⊢e1 :Int Γ⊢e2 :Int Γ⊢(Pluse1 e2):Int
Γ⊢e1 :Bool Γ⊢e2 :τ Γ⊢e3 :τ Γ⊢(Ife1 e2 e3):τ
(x : τ) ∈ Γ Γ,x : τ1,f : (τ1 → τ2) ⊢ e : τ2 Γ⊢x:τ Γ⊢(Recfunτ1 τ2 (f.x.e)):
Let’s implement a type checker.
54
The Functional Paradigm
MinHS
Static Semantics
Γ⊢(Numn):Int Γ⊢(Litb):Bool Γ⊢e1 :Int Γ⊢e2 :Int Γ⊢(Pluse1 e2):Int
Γ⊢e1 :Bool Γ⊢e2 :τ Γ⊢e3 :τ Γ⊢(Ife1 e2 e3):τ
(x : τ) ∈ Γ Γ,x : τ1,f : (τ1 → τ2) ⊢ e : τ2
Γ ⊢ x : τ Γ ⊢ (Recfun τ1 τ2 (f . x. e)) : τ1 → τ2
Γ ⊢ (Apply e1 e2) :
Let’s implement a type checker.
55
The Functional Paradigm
MinHS
Static Semantics
Γ⊢(Numn):Int Γ⊢(Litb):Bool Γ⊢e1 :Int Γ⊢e2 :Int Γ⊢(Pluse1 e2):Int
Γ⊢e1 :Bool Γ⊢e2 :τ Γ⊢e3 :τ Γ⊢(Ife1 e2 e3):τ
(x : τ) ∈ Γ Γ,x : τ1,f : (τ1 → τ2) ⊢ e : τ2
Γ ⊢ x : τ
Γ ⊢ (Recfun τ1 τ2 (f . x. e)) : τ1 → τ2 Γ⊢e1 :τ1 →τ2
Γ ⊢ (Apply e1 e2) :
Let’s implement a type checker.
56
The Functional Paradigm
MinHS
Static Semantics
Γ⊢(Numn):Int Γ⊢(Litb):Bool Γ⊢e1 :Int Γ⊢e2 :Int Γ⊢(Pluse1 e2):Int
Γ⊢e1 :Bool Γ⊢e2 :τ Γ⊢e3 :τ Γ⊢(Ife1 e2 e3):τ
(x : τ) ∈ Γ Γ,x : τ1,f : (τ1 → τ2) ⊢ e : τ2
Γ ⊢ x : τ
Γ ⊢ (Recfun τ1 τ2 (f . x. e)) : τ1 → τ2 Γ⊢e1 :τ1 →τ2 Γ⊢e2 :τ1
Γ ⊢ (Apply e1 e2) :
Let’s implement a type checker.
57
The Functional Paradigm
MinHS
Static Semantics
Γ⊢(Numn):Int Γ⊢(Litb):Bool Γ⊢e1 :Int Γ⊢e2 :Int Γ⊢(Pluse1 e2):Int
Γ⊢e1 :Bool Γ⊢e2 :τ Γ⊢e3 :τ Γ⊢(Ife1 e2 e3):τ
(x : τ) ∈ Γ Γ,x : τ1,f : (τ1 → τ2) ⊢ e : τ2
Γ ⊢ x : τ
Γ ⊢ (Recfun τ1 τ2 (f . x. e)) : τ1 → τ2 Γ⊢e1 :τ1 →τ2 Γ⊢e2 :τ1
Γ⊢(Applye1 e2):τ2
Let’s implement a type checker.
58
The Functional Paradigm
MinHS
Dynamic Semantics
Structural Operational Semantics (Small-Step) Initial states:
59
The Functional Paradigm
MinHS
Dynamic Semantics Structural Operational Semantics (Small-Step)
Initial states: All well typed expressions. Final states:
60
The Functional Paradigm
MinHS
Dynamic Semantics Structural Operational Semantics (Small-Step)
Initial states: All well typed expressions. Final states: (Num n), (Lit b),
61
The Functional Paradigm
MinHS
Dynamic Semantics Structural Operational Semantics (Small-Step)
Initial states: All well typed expressions. Final states: (Num n), (Lit b), Recfun too!
Evaluation of built-in operations:
e 1 → e 1′
(Plus e1 e2) → (Plus e1′ e2)
(and so on as per arithmetic expressions)
62
The Functional Paradigm
MinHS
Specifying If
e 1 → e 1′
(Ife1 e2 e3)→(Ife1′ e2 e3)
(If (Lit True) e2 e3) → e2 (If (Lit False) e2 e3) → e3
63
The Functional Paradigm
MinHS
How about Functions?
Recall that Recfun is a final state – we don’t need to evaluate it when it’s alone.
Evaluating function application requires us to:
1 Evaluate the left expression to get the function being applied
2 Evaluate the right expression to get the argument value
3 Evaluate the function’s body, after supplying substitutions for the abstracted variables.
e 1 → e 1′
(Apply e1 e2) → (Apply e1′ e2)
e 2 → e 2′
(Apply (Recfun...) e2) → (Apply (Recfun...) e2′)
The Functional Paradigm
MinHS
How about Functions?
Recall that Recfun is a final state – we don’t need to evaluate it when it’s alone.
Evaluating function application requires us to:
1 Evaluate the left expression to get the function being applied
2 Evaluate the right expression to get the argument value
3 Evaluate the function’s body, after supplying substitutions for the abstracted variables.
e 1 → e 1′
(Apply e1 e2) → (Apply e1′ e2)
e 2 → e 2′
(Apply (Recfun...) e2) → (Apply (Recfun...) e2′)
65
The Functional Paradigm
MinHS
How about Functions?
Recall that Recfun is a final state – we don’t need to evaluate it when it’s alone.
Evaluating function application requires us to:
1 Evaluate the left expression to get the function being applied
2 Evaluate the right expression to get the argument value
3 Evaluate the function’s body, after supplying substitutions for the abstracted variables.
e 1 → e 1′
(Apply e1 e2) → (Apply e1′ e2)
e 2 → e 2′
(Apply (Recfun...) e2) → (Apply (Recfun...) e2′)
v∈F
(Apply(Recfunτ1 τ2 (f.x.e))v)→e[x:=v,f :=(Recfunτ1 τ2 (f.x.e))]
66