Functional Programming - II

advertisement
CS 2104 – Prog. Lang. Concepts
Functional Programming II
Lecturer : Dr. Abhik Roychoudhury
School of Computing
From Dr. Khoo Siau Cheng’s lecture notes
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
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)) ;
Some remarks
• 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.
Outline
• More about Higher-order Function
• Type inference and Polymorphism
• Evaluation Strategies
• Exception Handling
Function with Multiple Arguments
• Curried functions accept multiple arguments
fun twice f x = f (f x) ;
Take 2 arguments
Curried function enables partial application.
let val inc2 = twice (fn x => x + x)
in (inc2 1) + (inc2 2)
end;
val it = 12 ;
Apply 1st argument
Apply 2nd argument
Curried vs. Uncurried
Curried functions
fun twice f x = f (f x) ;
twice (fn x => x+x) 3  12
Uncurried functions
fun twice’ (f, x) = f (f x) ;
twice’ (fn x => x+x, 3)  12
Curried Functions
Curried functions provide extra flexibility
to the language.
compose f g = fn x => f (g x)

compose f g x = f (g x)

compose f = fn g => fn x => f (g x)

compose = fn f => fn g => fn x => f (g x)
Types of Multi-Argument Funs
fun f(x,y) = x + y
f : int*int -> int
fun g x y = x + y
g : int -> int -> int
(g 3) : int -> int
((g 3) 4) : int
Function application is left associative; -> is right associative
Outline
• More about Higher-order Function
• Type inference and Polymorphism
• Evaluation Strategies
• Exception Handling
Type Inference
• ML expressions seldom need type declaration.
• ML cleverly infers types without much help from
the user.
2 + 2 ;
val it = 4 : int
fun succ n = n + 1 ;
val succ = fn : int -> int
Helping the Type Inference
• Explicit types are needed when type coercion is needed.
fun add(x,y : real)
fun add(x,y)
= x + y ;
= (x:real) + y;
val add = fn : real*real -> real
Every Expression has only One Type
fun f x = if x > 0 then x else [1,2,3]
val f = fn : Int -> ???
This is not type-able in ML.
• Conditional expression has the same type at
both branch.
fun abs(x) = if x>0 then x else 0-x ;
val abs = fn : int -> int
Example of Type Inference
tt1
t2t3
t
fun f
g
=
intt2
g
(g
t3
type(g) = t = intt2 = t2t3
int = t2, t2 = t3
type(g) = t = int  int
type(f) = t  t1 = t  t3
= (int  int)  int
1)
Three Type Inference Rules
(Application rule)
If f x : t,
then
x : t’ and
f : t’ -> t for some new type t’.
(Equality rule)
If both the types x : t and
x : t’ can be deduced for a variable x,
then
t = t’.
(Function rule)
If t  u = t’  u’,
then
t = t’ and u = u’.
Example of Type Inference
fun f g = g (g 1)
Let g : tg (g (g 1)) : trhs
So, by function declaration, we have
f : tg -> trhs
By application rule, let (g 1): t(g 1)
g (g 1) : trhs  g : t(g 1) -> trhs.
By application rule,
(g 1) : t(g
1)
 g : int -> t(g
By equality rule : t(g
1)=
1).
int = trhs.
By equality rule : tg = int -> int
Hence, f : (int -> int) -> int
Parametric Polymorphism
Type parameter
fun I x = x ;
val I = fn : ’a -> ’a
• A Polymorphic function is one whose type contains
type parameters.
• A poymorphic function can be applied to arguments
of more than one type.
(I 3)
(I [1,2])
(I square)
• Interpretation of val I = fn : ’a -> ’a
for all type ‘a, function I takes an input of type ‘a
and returns a result of the same type ‘a.
Polymorphic Functions
• A polymorphic function is one whose type contains
type parameter.
fun map f [] = []
|
map f (x::xs) = (f x) :: (map f xs)
Type of map : (’a->’b) -> [’a] -> [’b]
map (fn x => x+1) [1,2,3] => [2,3,4]
map (fn x => [x]) [1,2,3] => [[1],[2],[3]]
map (fn x => x) [“y”,“n”] => [“y”, “n”]
Examples of Polymorphic Functions
t5t6
t1t2t
t4
t4t5
fun compose f g = (fn x => f (g x))
t4t6
type(f) = t1 = t5t6
type(g) = t2 = t4t5
range(compose) = t = t4t6
type(compose) = t1t2t
= (t5t6)  (t4t5)  (t4t6)
Examples of Polymorphic Functions
fun compose f g = (fn x => f (g x))
Let x:tx f:tf g:tg so
compose: tf -> tg ->trhs
(fn x=>f (g x)):trhs => trhs = tx->t(f(gx))
(g x):t(gx) ==> g: tx->t(gx) and
tg = tx->t(g x)
(f (g x)):t(f(gx)) ==> f:t(gx)->t(f(gx)) and
tf = t(gx)->t(f(gx))
compose: (t(gx)->t(f(gx)))->(tx->t(gx))->(tx->t(f(gx)))
Rename the variables:
compose: (’a->’b)->(’c->’a)->(’c->’b)
Outline
• More about Higher-order Function
• Type inference and Polymorphism
• Evaluation Strategies
• Exception Handling
Approaches to Expression Evaluation
• Different approaches to evaluating an expression
may change the expressiveness of a programming
language.
• Two basic approaches:
– Innermost (Strict) Evaluation Strategy
• SML, Scheme
– Outermost (Lazy) Evaluation Strategy
• Haskell, Miranda, Lazy ML
Innermost Evaluation Strategy
formals
let fun f x = x + 1 + x
in f (2 + 3) end ;
actuals
body
• To Evaluate the call <name><actuals> :
– (1) Evaluate <actuals> ;
– (2) Substitute the result of (1) for the formals in
the body ;
– (3) Evaluate the body of <name> ;
– (4) Return the result of (3) as the answer.
fun f x = x + 2 + x ;
f (2+3)
==> f (5)
==> 5 + 2 + 5
==> 12
fun g x y = if (x < 3) then y else x;
g 3 (4/0) ==> g 3 
==> 
• Also referred to as call-by-value evaluation.
• Occasionally, arguments are evaluated unnecessarily.
Outermost Evaluation Strategy
• To Evaluate <name><actuals> :
• (1) Substitute actuals for the formals in the body ;
• (2) Evaluate the body ;
• (3) Return the result of (2) as the answer.
fun f x = x + 2 + x ;
f (2+3) ==> (2+3) + 2 + (2+3)
==> 12
fun g x y = if x < 3 then y else x ;
g 3 (4/0) ==> if 3 < 3 then (4/0) else 3
==> 3
It is possible to eliminate redundant computation in
outermost evaluation strategy.
fun f x = x + 2 + x ;
f (2+3) ==> x + 2 + x
x=(2+3)
==> 5 + 2 + x
5
==> 7 + x
==> 7 + 5
==> 12
Note: Arguments are evaluated only when they are needed.
Why Use Outermost Strategy?
• Closer to the meaning of mathematical functions
fun k x y = x ;
val const1 = k 1 ;
val const2 = k 2 ;
• Better modeling of real mathematical objects
val naturalNos =
let fun inf n = n :: inf (n+1)
in inf 1
end ;
Hamming Number
List, in ascending order with no repetition, all
positive integers with no prime factors other than
2, 3, or 5.
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,...
n as a prime factor
fun scale n []
= []
|
scale n (x::xs) = (n*x) :: (scale n xs)
scale 2 [1,2,3,4,5] = [2,4,6,8,10]
scale 3 [1,2,3,4,5] = [3,6,9,12,15]
scale 3 (scale 2 [1,2,3,4,5])
= [6,12,18,24,30]
Merging two Streams
fun merge []
[]
= []
|
merge (x::xs) (y::ys) =
if x < y then x :: merge xs (y::ys)
else if x > y then y :: merge (x::xs) ys
else x :: merge xs ys
merge [2,4,6] [3,6,9]
= [2,3,4,6,9]
Hamming numbers
1
scale 2
merge
::
merge
scale 3
scale 5
val hamming = 1 ::
merge (scale 2 hamming)
(merge (scale 3 hamming)
(scale 5 hamming))
Outline
• More about Higher-order Function
• Type inference and Polymorphism
• Evaluation Strategies
• Exception Handling
Exception Handling
• Handle special cases or failure (the exceptions)
occurred during program execution.
hd [];
uncaught exception hd
• Exception can be raised and handled in the program.
exception Nomatch;
exception Nomatch : exn
fun member(a,x) =
if null(x) then raise Nomatch
else if a = hd(x) then x
else member(a,tl(x))
fun member(a,x) =
if null(x) then raise Nomatch
else if a = hd(x) then x
else member(a,tl(x))
member(3,[1,2,3,1,2,3]) ;
val it = [3,1,2,3] : int list
member(4,[]) ;
uncaught exception Nomatch
member(5,[1,2,3]) handle Nomatch=>[];
val it = [] : int list
Conclusion
• More about Higher-order Function
– Curried vs Uncurried functions
– Full vs Partial Application
• Type inference and Polymorphism
– Basic Type inference rules
– Polymorphic functions
• Evaluation Strategies
– Innermost
– Outermost
• Exception Handling is available in ML
Download