CS 2104 : Prog. Lang. Concepts. Functional Programming I Lecturer : Dr. Abhik Roychoudhury School of Computing From Dr. Khoo Siau Cheng’s lecture notes Outline • • • • • What is Functional Programming? Basic Expressions Using Higher-order Functions List Processing Value and Type Constructions Features of Functional Programming • • • • Expressions: The main construct in the language Functions : First-class citizens of the language Types : for specification and verification List Processing Computation • Achieved via function application • Functions are mathematical functions without side-effects. – Output is solely dependent of input. States Pure function Impure function with assignment Outline • • • • • What is Functional Programming? Basic Expressions Using Higher-order Functions List Processing Value and Type Constructions Basic SML Expressions <Exp> ::= <Constants> | <Identifiers> | <Exp1> <op> <Exp2> | | if <Exp0> then <Exp1> else <Exp2> | let {<Decl>} in <Exp> end | fn <Pattern> {<Pattern>} => <Exp> | <Exp1> <Exp2> | (<Exp1>, <Exp2> {, <Exp3>}) | <List-Exp> <Constants> ::= all constants <Identifiers> ::= all identifiers <op> ::= all binary operators If-expression, Not If-statement if <e0> then <e1> else <e2> if (3 > 0) then 4+x else x-4 ; 4+x If-expression evaluates to a value. Constructing a Linear List <List-Exp> ::=[[<Exp> {, <Exp>}] ] | nil | <Exp> :: <Exp> nil ; Note : A list must end with nil. 1::nil ; 1::(2::nil); (x+2)::(y-3)::(x*3)::nil ; Constructing a Linear List <List-Exp> ::=[[<Exp> {, <Exp>}] ] | nil | <Exp> :: <Exp> nil ; Operator :: is right associative. 1::nil ; 1::(2::nil); (x+2)::(y-3)::(x*3)::nil ; head tail [] ; [1] ; [1,2]; [x+2,y-3,x*3] ; Constructing a Tuple (<Exp> , <Exp> {, <Exp>}) Tuple is like a record without field names. (1,2) ; (1,2,True,4,False) ; (x+2,y=3,x*3) ; Giving An Expression A Name Declaring an expression with a name enables easy reference. <decl> ::= val <pattern> = <exp> Identifiers are legally declared after proper pattern matching. val val val val x = 3 (y,z) [a,b] [a,b] ; = (x+3,x-3) ; = [x+2,y-3] ; = [x+2] ; val (x::xs) = [1,2,3,4,5] ; Pattern-Match a List val (x::xs) = [1,2,3,4,5] ; x 1 xs [2,3,4,5] ; val (x::_::y::xs) = [1,2,3,4,5] ; x 1 y 3 xs [4,5] ; Local Declaration/Definition let {<decl>} in <exp> end let val x = (3,4) val (a,b) = x in a + a * b end ; 15 Identifiers are statically scoped Nested bindings of same var. • Let val x =2 in let val x = x +1 in x*x end end • Let val x = 2 in let val y = x +1 in y * y end end • Let val x = 2 in (x+1)*(x+1) end • 9 Function Declaration and Function Application <Decl> ::= fun <var> <para> = <exp> <para> ::= <pattern> {<pattern>} fun fac x = if (x > 0) then x * (fac (x-1)) else 1 fac 2 if (2 > 0) then 2 * (fac (2-1)) else 1 2 * (fac 1) 2 * (if (1 > 0) then 1 * (fac (1-1)) else 1) 2 * (1 * (fac 0)) 2 * (1 * 1) 2 Function as Equations A function can be defined by a set of equations. <Decl> ::= fun <var> <para> = <exp> {| <var> <para> = <exp>} fun fac 0 | fac n = 1 = n * (fac (n-1)) Function application is accomplished via pattern matching. fac 2 2 * (fac (2-1)) 2 * (1 * (fac (1-1))) 2 * (1 * 1) 2 * (fac 1) 2 * (1 * (fac 0)) 2 Outline • • • • • What is Functional Programming? Basic Expressions Using Higher-order Functions List Processing Value and Type Constructions Functions as First-Class Citizens Lambda abstraction: A nameless function fn <para> => <Exp> (fn (x,y) => x + y) (2,3) 2+3 5 Definition Application val newfn = fn (x,y) => x + y ; newfn(2,4) + newfn(4,3) Functions as First-Class Citizens Passing function as argument fun apply (f, x) = f x apply (fac, 3) fac 3 6 apply (fn x => x*2, 3) (fn x => x*2) 3 3*2 6 Functions as First-Class Citizens Returning function as result fun choose (x,f,g) = if x then f else g ; choose(a = b,fn x => x*2, fac)(4) returns either 8 or 24 fun compose (f,g) = fn x => f (g x) (compose (add3,fac)) 4 (fn x => add3 (fac x))(4) (add3 (fac 4)) compose (f,g) is denoted as f o g 27 Functions as First-Class Citizens Storing function in data structure val flist = [ fac, fib, add3] ; let val [f1,f2,f3] = flist in (f1 2) + (f2 3) + (f3 4) end ; Functions as Algorithms • A function describes a way for computation. • Self-Recursive function fun factorial (x) = if (x <= 1) then 1 else x * factorial (x-1) ; • Mutual-Recursive functions fun | | and even(0) even(1) even(x) odd(x) = true = false = odd(x-1) = even(x-1) ; Passing Arguments to a Function How many argument does this function really take? fun f (x,y) = x + 2 * y ; a pair Calling f with argument (1,2) yields 5. fun g x y = x + 2 * y ; pattern 1 pattern 2 Calling g with arguments 1 and 2 yields 5. Outline • • • • • What is Functional Programming? Basic Expressions Using Higher-order Functions List Processing Value and Type Constructions Exploring a List fun length [] = 0 | length (x::xs) = 1 + length(xs) ; length([10,10,10]) ==> ==> ==> ==> ==> 1 1 1 1 3 + + + + length([10,10]) 1 + length([10]) 1 + 1 + length([]) 1 + 1 + 0 Note how a list is being consumed from head to tail. fun append([],ys) = ys | append(x::xs,ys) = x :: append(xs,ys); append([1,2],[3]) ==> 1::append([2],[3]) ==> 1::2::append([],[3]) ==> 1::2::[3] ==> [1,2,3] Note how a new list is being produced from head to tail. append(x,y) is denoted by x @ y. fun rev(xs) = let fun r([],ys) = ys | r(x::xs,ys)= r(xs,x::ys) in r(xs,[])end rev([1,2,3]) ==> r([1,2,3],[]) ==> r([2,3],1::[]) ==> r([3],2::1::[]) ==> r([],3::2::1::[]) ==> [3,2,1] Note how lists are being produced and consumed concurrently. Map operation map square [1,2,3,4] ==> [square 1, square 2,.., square 4] ==> [1,4,9,16] fun map f [] = [] | map f (x::xs) = (f x) :: (map f xs) map square [1,2] ==> (square 1) :: (map square [2]) ==> 1 :: (square 2) :: (map square []) ==> 1 :: 4 :: [] == [1,4] Map operation fun map f [] = [] | map f (x::xs) = (f x) :: (map f xs) Law for Map : map f (map g list) = map (f o g) list map f (map g [a1,..,an]) = map f [g a1,..,g an] = [f (g a1),.., f (g an)] = [(f o g) a1,.., (f o g) an] = map (f o g) [a1,..an] Reduce Operation reduce [a1,..,an] a0 = a1 (a2 (.. (an a0))) reduce f [a1,..,an] a0 = f(a1,f(a2,f(..,f(an,a0)))) : a1 a2 a1 : a2 : : an [] an a0 reduce (op *) [2,4,6] 1 ==> 2 * (4 * (6 * 1)) ==> 48 reduce (fn (x,y)=>1+y) [2,4,6] 0 ==> 1 + (1 + (1 + 0)) ==> 3 + : 2 1 : 4 1 : 6 + [] + 1 0 Outline • • • • • What is Functional Programming? Basic Expressions Using Higher-order Functions List Processing Value and Type Constructions Types: Classification of Values and Their Operators Basic Types Type bool int real string Values true,false …,~1,0,1,2,… ..,0.0,.,3.14,.. “foo”,”\”q\””,… Operations =, <>, … =,<>,<,+,div,… =,<>,<,+,/,… =,<>,… Boolean Operations: e1 andalso e2 e1 orelse e2 Types in ML • Every expression used in a program must be welltyped. – It is typable by the ML Type system. • Declaring a type : 3 : int [1,2] : int list • Usually, there is no need to declare the type in your program – ML infers it for you. Structured Types Structured Types consist of structured values. • Structured values are built up through expressions. Eg : (2+3, square 3) • Structured types are denoted by type expressions. <type-expr> ::= <type-name> | <type-constant> | <type-expr> * <type-expr> | <type-expr> <type-expr> | <type-expr> list |… Type of a Tuple (1,2) : int * int (3.14159, x+3,true) : real * int * bool A * B = set of ordered pairs (a,b) Data Constructor : Type Constructor : * In general, (a1,a2,…,an) (,) as in belongs to as in (a,b) A* B A1*A2*…*An. Type of A List Type Constructor : list [1,2,3] : int list [3.14, 2.414] : real list Not well-typed!! [1, true, 3.14] : ?? A list = set of all lists of A -typed values. A in A-list refers to any types: (int*int) list : [ ], [(1,3)], [(3,3),(2,1)], … int list list : [ ], [[1,2]], [[1],[0,1,2],[2,3],… Function Types Declaring domain & co-domain fac : int -> int A -> B = set of all functions from A to B. Type Constructor : -> Data Construction via : 1. Function declaration : 2. Lambda abstraction : fun f x = x + 1 ; fn x => x + 1; Value Selection via function application: f3 4 (fn x => x + 1) 3 4 Sum of Types Enumerated Types datatype Days New Type = Mo | Tu | We | Th | Fr | Sa | Su ; data / data constructors Selecting a summand via pattern matching: case d of Sa => “Go to cinema” | Su => “Extra Curriculum” | _ => “Life goes on” Combining Sum and Product of Types: Algebraic Data Types Defining an integer binary tree: datatype IntTree = Leaf int | Node of (IntTree, int, IntTree) ; fun height (Leaf x) = 0 | height (Node(t1,n,t2))= 1 + max(height(t1),height(t2)) ; Conclusion • A functional program consists of an expression, not a sequence of statements. • Higher-order functions are first-class citizen in the language. – It can be nameless • List processing is convenient and expressive • In ML, every expression must be well-typed. • Algebraic data types empowers the language.