Functional Programming: a programming paradigm based on functions
Programming Paradigm: classification of programming approach based behaviour of code
Programming Paradigm: classification of programming approach based behaviour of code
Features of functional languages
Program State: the state of the machine at any given time
Typically described as the contents of variables
# imperative.py x = x + 1 a[0] = 42
Imperative: State is mutable, changing or destructive
Imperative: State is mutable, changing or destructive
Can cause side effects (which is bad)
# side_effects.py count = 0 def f(node) global count node.data = count count+=1 return count end
No Referential transparency: replace expression with value with no side effects
f(x) + f(x) + f(x) != 3 * f(x) != 1 + 2 + 3
Imperative: State is mutable, changing or destructive
Reality Check: No single state exists
# states.c int x = 1; if (fork() == 0) x = x + 1; else x = x * 5; wait(NULL) printf("x: %d\n", x);
Functional Programming has immutable state
Functional Programming uses immutable state
Declarative Programming
# imperative.py def evens(arr): ret = [] for x in arr remainder = x % 2 if remainder == 0: ret.append(x) return ret
as opposed to
declarative.py def evens(arr): ret = [x for x in arr if x % 2 == 0] return ret
Our First Ocaml Program
(* hello.ml *) print_string "Hello World!\n"
OCaml is a compiled language
ocamlc hello.ml
Helpful Programs
Probably want to run dune utop src
Will need to use ;; to end epxressions in utop
Rememeber Syntax vs Semantics
Everything is an expression (e)
1 + 3
the expression 1+3 has type int
true
the value true has type bool
Expressions have types
Expressions have types
The if expression
(if e1:bool then e2:t else e3:t):t
Actual Syntax:
if e1 then e2 else e3
Static and Latent Typing
Expressions have types
functions are expressions
(* function.ml *) let f x = if x mod 2 = 0 then 1 else 0 in f ;;
The expression f has type int->int
Type Inference: inferring a variable's type
(* function.ml *) let f x = if x mod 2 = 0 then 1 else 0 in f ;;
Function Definition Syntax
let f x1 ... xn = e
Function Calling Syntax
f x1 ... xn
Function Calling Syntax
f x1 ... xn
Things that happen
(* factorial.ml *) let rec f n = if n = 0 then 1 else n * fact (n-1) in f ;; f 2;;
Type of f: int -> int
Type of f 2:int
Value of f 2: 2
More on Type Checking
Types are inferred by operations they use
(* types.ml *) (* compare two same types *) 1 = 1 x = y x > y x < y (* x and y must have the same type *) (* int have operators *) 3 + 4 x - y x * y x / y x mod y (* floats have different ones *) 3.2 +. 4. x -. y x *. y x /. y (* Strings have some too*) "hello" ^ " world" (* latent typing means inference *) let f x y = if x > y then x else y-4;; (* int -> int -> int *)
(* types.ml *) (* compare two same types *) 1 = 1 x = y x > y x < y (* x and y must have the same type *) (* int have operators *) 3 + 4 x - y x * y x / y x mod y (* floats have different ones *) 3.2 +. 4. x -. y x *. y x /. y (* Strings have some too*) "hello" ^ " world" (* latent typing means inference *) let f x y = if x > y then x else y-4;; (* int -> int -> int *)
(* types.ml *) (* compare two same types *) 1 = 1 x = y x > y x < y (* x and y must have the same type *) (* int have operators *) 3 + 4 x - y x * y x / y x mod y (* floats have different ones *) 3.2 +. 4. x -. y x *. y x /. y (* Strings have some too*) "hello" ^ " world" (* latent typing means inference *) let f x y = if x > y then x else y-4;; (* int -> int -> int *)
(* types.ml *) (* compare two same types *) 1 = 1 x = y x > y x < y (* x and y must have the same type *) (* int have operators *) 3 + 4 x - y x * y x / y x mod y (* floats have different ones *) 3.2 +. 4. x -. y x *. y x /. y (* Strings have some too*) "hello" ^ " world" (* latent typing means inference *) let f x y = if x > y then x else y-4;; (* int -> int -> int *)
(* types.ml *) (* compare two same types *) 1 = 1 x = y x > y x < y (* x and y must have the same type *) (* int have operators *) 3 + 4 x - y x * y x / y x mod y (* floats have different ones *) 3.2 +. 4. x -. y x *. y x /. y (* Strings have some too*) "hello" ^ " world" (* latent typing means inference *) let f x y = if x > y then x else y-4;; (* int -> int -> int *)
(* let.ml *) let x = e1 in e2
Let expressions are expressions
Expressions have a type
(* let-type.ml *) (let x = e1:t1 in e2:t2):t2
Can be nested
(* let-nest.ml *) let x = 3 in let y = 4 in x + y
Can be used for local variables
(* let-vars.ml *) let area r = let pi = 3.14 in pi *. r *. r
Variables will be shadowed
(* let-shadowing.ml *) let x = 3 in let x = 5 in x + 4
Lists are the basic data structure in OCaml
Lists are the basic data structure in OCaml
(* lists.ml *) [1;2;3;4;5]
List Creation
(* lists-1.ml *) e1::e2::[]
Lists are the basic data structure in OCaml
(* lists-1.ml *) e1::e2::[]
Have type list
When evaluating, go right to left
Can deconstruct lists
(* match-nest.ml *) let x = [1;2;3] in match x with |[] -> true |h::t -> false
Match looks at patterns of structure
Match looks at patterns of structure
Common Patterns
(* match-patterns.ml *) let empty x = in match x with |[] -> true (* empty *) |a::[] -> false (* list of size 1 *) |h::t -> false (* list at least size 1 *) |_ -> false (* wildcard *)
(* match-patterns.ml *) let empty x = in match x with |[] -> true (* empty *) |a::[] -> false (* list of size 1 *) |h::t -> false (* list at least size 1 *) |_ -> false (* wildcard *)
(* match-patterns.ml *) let empty x = in match x with |[] -> true (* empty *) |a::[] -> false (* list of size 1 *) |h::t -> false (* list at least size 1 *) |_ -> false (* wildcard *)
(* match-patterns.ml *) let empty x = in match x with |[] -> true (* empty *) |a::[] -> false (* list of size 1 *) |h::t -> false (* list at least size 1 *) |_ -> false (* wildcard *)
(* match-patterns.ml *) let empty x = in match x with |[] -> true (* empty *) |a::[] -> false (* list of size 1 *) |h::t -> false (* list at least size 1 *) |_ -> false (* wildcard *)
(* match-patterns.ml *) let empty x = in match x with |[] -> true (* empty *) |a::[] -> false (* list of size 1 *) |h::t -> false (* list at least size 1 *) |_ -> false (* wildcard *)
Variables are bound on order
Last item is a list
Match looks at patterns of structure
Can be put as argument
(* match-function.ml *) let car (h::_) = h;; let cdr (_::t) = t;;
Match looks at patterns of structure
Can be Polymorphic
(* match-polymorphic.ml *) let car lst = match lst with [] -> [] h::_ -> h;; (* lst has type 'a list *) let rec sum lst = match lst with []-> 0 |h::t -> h + sum t;; (* h has type int list *)
(* match-polymorphic.ml *) let car lst = match lst with [] -> [] h::_ -> h;; (* lst has type 'a list *) let rec sum lst = match lst with []-> 0 |h::t -> h + sum t;; (* h has type int list *)
Used commonly in recursive functions
(* match-rec-functions.ml *) let rec sum lst = match lst with []-> 0 |h::t -> h + sum t;; let rec negate lst = match lst with []-> [] |h::t -> -h + negate t;; let rec last lst = match lst with [x]-> x |h::t -> last t;; let rec append l m = match l with []-> m |h::t -> x :: (append t m) let rec rev l = match l with |[] -> [] | h::t -> append (rev t) (h::[]) (* rev is O(n^2) *) (* can you do better? *)
(* match-rec-functions.ml *) let rec sum lst = match lst with []-> 0 |h::t -> h + sum t;; let rec negate lst = match lst with []-> [] |h::t -> -h + negate t;; let rec last lst = match lst with [x]-> x |h::t -> last t;; let rec append l m = match l with []-> m |h::t -> x :: (append t m) let rec rev l = match l with |[] -> [] | h::t -> append (rev t) (h::[]) (* rev is O(n^2) *) (* can you do better? *)
(* match-rec-functions.ml *) let rec sum lst = match lst with []-> 0 |h::t -> h + sum t;; let rec negate lst = match lst with []-> [] |h::t -> -h + negate t;; let rec last lst = match lst with [x]-> x |h::t -> last t;; let rec append l m = match l with []-> m |h::t -> x :: (append t m) let rec rev l = match l with |[] -> [] | h::t -> append (rev t) (h::[]) (* rev is O(n^2) *) (* can you do better? *)
(* match-rec-functions.ml *) let rec sum lst = match lst with []-> 0 |h::t -> h + sum t;; let rec negate lst = match lst with []-> [] |h::t -> -h + negate t;; let rec last lst = match lst with [x]-> x |h::t -> last t;; let rec append l m = match l with []-> m |h::t -> x :: (append t m) let rec rev l = match l with |[] -> [] | h::t -> append (rev t) (h::[]) (* rev is O(n^2) *) (* can you do better? *)
Like Lists, but not really
(* tuples.ml *) (1,2)
Tuples have a set Type
(* tuples-type.ml *) (1,2) (* int * int *) (1,"string",2.3) (* int * string * float *) ('a','b') (* char * char *) ['a';'b'] (* char list *) [(1,2);(3,4)] (* (int * int) list *) ([1;2],[3;4]) (* int list * int list *)
Can Pattern Match
(* tuples-match.ml *) let add t = match t with (a,b) -> a + b
Remember Tuples have a type based on size
(* tuples-match-err.ml *) let add t = match t with (a,b) -> a + b |(a,b,c) -> a + b + c
Like a weird hash
(* records.ml *) type data = { month: string; dat: int; year: int };; let today = { day=29; year=2020; month="feb"};;
O in OCaml stands for Object
(* record-access-1.ml *) print_string today.month
Can also pattern match
(* record-access-2.ml *) let { month=_; day = d} = today in print_int d
We just saw this syntax
(* ud-types-alias.ml *) type ilist = int list;; let f x:ilist = [1;2;3;4];;
Ultimately not really useful in this form
Variant Types are more useful
(* ud-variants.ml *) type parity = Even | Odd
Like an enum
(* pm-variants.ml *) let swap x = match x with Even -> Odd |Odd -> Even
Can be Pattern Matched
Can Hold Data
(* ud-variants-1.ml *) type parity = Even of int | Odd of int
Can still be Pattern Matched
(* pm-variants-1.ml *) let add x = match x with Even(x) -> Odd(x+1) |Odd(x) -> Even(x+1)
Can Hold Data
Can be different
(* ud-variants-2.ml *) type shape = Rect of int * int | Circle of float
Can still be Pattern Matched
(* pm-variants-2.ml *) let area s = match s with Rect (w,l) -> float_of_int (w*l) |Circle r -> r *. r *. 3.14
Can be Recursive
(* llist.ml *) type linked = Item of string * linked |Null;; let head lst = match lst with Item(x,_) -> x |Null -> "";; head (Item("Hello",Item("world", Null)));;
Can be generic
(* some_none.ml *) type 'a option = Some of 'a |None
Built into OCaml