CS 541 Lecture * Outline *
* Functions, a builtin, firstclass type of SML (chapter 5, up to 5.11)
** fn makes functions
this is called lambda in Scheme and LISP
a block in Smalltalk
Used for avoiding redundancy in code (functional abstraction)
and for tool building
*** examples:

fn MAKES FUNCTIONS (CLOSURES)
 (fn x => x)("y");
val it = "y" : string
 (* LISP style invocation *)
((fn x => hd x) [1,2,3]);
val it = 1 : int

the function (fn x => hd x) is the same as "hd"

 ((fn (x,y) => 0) ("a","b"));
val it = 0 : int
 ((fn () => 5));
val it = fn : unit > int
 (fn () => 5)();
val it = 5 : int

talk about parenthesization; LISP style seems clearest to me
*** applicative order evaluation rule

APPLICATIVE ORDER EVALUATION
((fn x => e1) e2)
=def=
let val x = e2 in e1 end
examples:
((fn z => z * z + 1) 7)
=
let val z = 7 in z * z + 1 end
((fn (x,y) => x*y + 3) (5,6))
=
let val x = 5
and y = 6
in x*y + 3 end
=
let val (x,y) = (5,6)
in x*y + 3 end

so it's the value of e1,
in environment where x denotes value of e2
i.e., Val(((fn x => e1) e2), env)
= Val(e1, env + {x > Val(e2,env)})
Typically in semantics we think of function application as primitive,
and consider let to be sugar for the function application.
draw picture
v\
global env > [ +: ... ] 
 parent
____________
 *  * 
_________

v
parameters: z
body: z * z + 1

FUNCTION CLOSURES
def: a *closure* is a
function together with the
environment in which the fn
was evaluated.

like salmon, a function returns to the enivronment of its birth
when it's called upon

 val inc =
let val one = 1
in
(fn x => x + one)
end;
val inc = fn : int > int
 let val one = 2
in inc(3)
end;
val it = 4 : int

Note, toplevel definition produces new environment at top level
*** environment model
draw pictures:
______________________ ___________
global env >  +: *>  
 inc: *  ________
___________________
^  ^
  parent
parent  __________
_____________   
 one: 2    one: 1 
____________  __________
inc(3)  ^
 
_____v______
 *  * 
_________

v
parameters: x
body : x + one
*** the point
the point of all this machinery is to enforce static scoping.
Another way to think about it is that this machinery makes it
so your function does what you think it does looking at its def
** function sugars revisited
*** sugar for simple fun bindings

FUNCTION SUGARS
fun fact 0 = 1
 fact n = n * fact(n1);
(* equivalent to *)
fun fact x =
(case x of
0 => 1
 n => n * fact(n1));
(* equivalent to *)
val rec fact =
fn x =>
(case x of
0 => 1
 n => n * fact(n1));

so in semantics, we can ignore "fun" and only consider val bindings,
recall we can desugar pattern matches so all function defs look like
fun name args = E
so sugar is
fun name args = E
=def=
val rec name = fn args => E
*** val rec bindings
binds names (like fact) to values of expressions,
evaluated in an environment that includes bindings for
the names.
So the values had better be closures,
otherwise not defined!
draw the following picture
v\
global env > [ fact: ]\ 
  parent
___v________
 *  * 
_________

v
parameters: x
body: (case x of 0 => 1  n => n * fact(n1))
** Functions firstclass in SML
(fn x => e1)
denotes functional abstraction of e1.
*** examples

SOME SYNTACTIC GAMES
 (fn x => x); (* identity function *)
val it = fn : 'a > 'a
 val id = (fn x => x);
val id = fn : 'a > 'a
 fun id x = x;
val id = fn : 'a > 'a
 (id 1);
val it = 1 : int
 id(1);
val it = 1 : int
 id 1;
val it = 1 : int
 id;
val it = fn : 'a > 'a

Q: how is the following parsed? How to get it to go the other way?

 id id 3;

Q: how is the following parsed?

 val inc = (fn y => y + 1);
val inc = fn : int > int
 inc 3 * 4;

*** functionals
here we start to see how functionals are useful for tool making

FUNCTIONALS
val hdtl = fn lst => hd (tl lst);
fun checkleft t = check (left t);

identify the common parts of this pattern,
and pass the changing parts as arguments

(* compose functional *)
fun compose (f,g) x = f (g x);
val compose =
(fn (f,g) =>
(fn x => f(g x)));
(* type is:
(('a > 'b) * ('c > 'a)) > 'c > 'b *)

compose is builtin to SML as infix o (lowercase letter "oh")

val hdtl = compose(hd,tl);

Compare with
val hdtl = fn ls => compose(hd,tl) ls;
Q: what's the type of the above?
Might mention the eta rule...

hdtl [1,2,3];
compose(hd,tl) [1,2,3];
compose(hd,compose(tl,tl)) [1,2,3,4];

might trace hdtl [1,2,3] using equations if questions
Q: how to write checkleft using compose?
look at parenthesization of these last two lines...
Q: can you define a function B such that
B hd tl = hdtl
?
note: this really means (B hd) tl
look at the equations: want
B hd tl = hdtl = compose(hd,tl)
so one way to define this is
fun B f g = compose(f,g);
But now, what good are f and g doing here?
That is, if we want to write
val B = (C compose)
then we must have...
B hd tl
=
(C compose) hd tl
=
compose(hd,tl)
so we can write...
fun C compose hd tl = compose(hd,tl);

(* THE CURRY FUNCTIONAL *)
fun curry compose hd tl = compose(hd,tl);
val curry =
(fn f =>
(fn x =>
(fn y => f(x,y))));
val by2 = ((curry (op *)) 2);
(by2 5);
val append = op @;
(((curry append) [1,2]) [3,4]);

one use of curried functions is as "partial application" as shown by
the function "by2".
This prevents writing 2 redundantly in code...
Q: can you write a function in C which is a curried multiplication?
have to explicitly represent the closure
A good use of curried functions is for defining tool makers...

(* MAPPING *)
fun add_scalar (s:int) [] = []
 add_scalar s (x::ls) =
(s+x)::(add_scalar s ls);
fun subst_all new old [] = []
 subst_all new old (x::ls) =
(if x = old then new else x)
:: (subst_all new old ls);
fun map f [] = []
 map f (x::ls) = (f x)::(map f ls);
fun map f lst =
let fun helper [] = []
 helper (x::ls) =
(f x)::(helper ls);
in helper lst
end;

the questions to ask when generalizing from examples:
what parts change? (make them into arguments)
what parts stay the same?

FOR YOU TO DO
Recall:
datatype 'a tree =
Lf
 Br of 'a * 'a tree * 'a tree;
Generalize:
fun preorder Lf = []
 preorder (Br(v,t1,t2)) =
[v] @ preorder t1 @ preorder t2;
fun inc Lf = Lf
 inc (Br(v,t1,t2)) =
Br(v+1, inc t1, inc t2);

now a little physics...

PHYSICS FOR FUNCTIONAL PROGRAMMERS
(* FIELDS ARE LIKE CURRIED FUNCTIONS *)
fun gravfield m1 r_vec m2 =
let val G = 6.670E~11 (* N*m^2/kg^2 *)
fun force r =
if r = 0.0 then 0.0
else (~G * m1 * m2) / (r*r)
in
map force r_vec (* : N list *)
end;
val earths_field =
let val mass_of_earth = 5.96E24 (*kg*)
val rad_of_earth = 6.370E9 (*m*)
in
gravfield mass_of_earth
[0.0,0.0,rad_of_earth]
end;

so physics uses higherorder concepts like curried functions!

TYPES OF CURRIED FUNCTION APPLICATIONS
EXPRESSION TYPE
gravfield : kg > (m list > (kg > N))
5.96E24 : kg
(gravfield 68.0) : m list > (kg > N)
[0.0,0.0,6.0E6] : m list
((gravfield 68.0) [0.0,0.0,6.0E6])
: kg > N
68.0 : kg
(((gravfield 5.96E24) [0.0, 0.0, 6.0E6])
68.0) : N

this is why the precendence of f x y is ((f x) y)
and of a > b > c is (a > (b > c))

(* COMBINATORS (WITH HISTORICAL NAMES) *)
val B = (curry compose);
fun B f g x = f(g x);
fun W f x = ((f x) x);
val twice = (W B);
fun by2 x = 2 * x;
((twice by2) 7);

((twice by2) 7)
=
(((W B) by2) 7)
=
(((B by2) by2) 7)
=
(by2 (by2 7))
=
28

fun I x = x;
fun K c x = c;
((K 3) 5);
fun S f g x = ((f x) (g x));
(* type of S is:
('a > 'b > 'c)
> ('a > 'b)
> 'a > 'c *)
(* exercise: what is ((S K) K)? *)

Anything you can program, you can do with S and K (only)!
(if S had the right type, but it doesn't)

(* FIXPOINT COMBINATOR *)
fun Y F x = F (Y F) x;
(* type is:
(('a > 'b) > ('a > 'b))
> ('a > 'b) *)
fun Fact f n =
if n = 0 then 1 else n * f(n1);
val factorial = Y Fact
factorial 3;

Note that Fact is not recursive!
but factorial is the factorial function!
factorial 3
=
Y Fact 3
=
Fact (Y Fact) 3
=
if 3 = 0 then 1 else 3 * (Y Fact)(31); (* can skip this step *)
= (* can skip this step *)
3 * (Y Fact)(2)
=
3 * Fact (Y Fact) 2
=
3 * 2 * (Y Fact)(1)
=
3 * 2 * Fact (Y Fact) 1
=
3 * 2 * 1 * (Y Fact)(0)
=
3 * 2 * 1 * Fact (Y Fact) 0
=
3 * 2 * 1 * 1
=
6
** functions are the ultimate
these ideas will be explored more fully later.
*** can be used to implement "infinite" data strucutures
because a function describes a potentially infinite mapping
streams
*** can be used to implement arbitrary control structures.
because a function can represent the rest of the program
continuations