Advanced Functional Programming
Tim Sheard 1Lecture 17
Advanced Functional Programming
Tim Sheard Oregon Graduate Institute of Science & Technology
Lecture: Staged computation •Difference between Haskell and ML•Intro to MetaML•Using staging to control evaluation order
Advanced Functional Programming
Tim Sheard 2Lecture 17
Differences in Types
type variables and type application'a Tree
type constructorsint, string, bool
tuples(int * string * int)
type variables and type applicationTree a
type constructorsInt, String, Bool
tuples(Int, String, Bool)
Advanced Functional Programming
Tim Sheard 3Lecture 17
Differences
(* Data definitions *)
datatype 'a Tree = Tip of 'a | Fork of ('a Tree)*('a Tree)
'a = type variableconstructors are not capitalized
-- data definitions
data Tree a = Tip a | Fork (Tree a) (Tree a)
comments
Type constructors are post fix
constructor functions are always uncurried (use tuples)
note use of "of"
Advanced Functional Programming
Tim Sheard 4Lecture 17
difference 2
(* function definitions *)
fun fact n = if n=0 then 1 else n * (fact (n-1));
fun depth (Tip a) = 0 | depth (Fork(x,y)) = 1 + max (depth x, depth y);
-- function definitions
fact n = if n==0 then 1 else n * (fact (n-1))
depth (Tip a) = 0depth (Fork x y) = 1 + max (depth x) (depth y)
note use of "fun"equality is double
=
use "bar" (|) to separate clauses
primitives more likely to be curried in
Haskell
Advanced Functional Programming
Tim Sheard 5Lecture 17
Data List a = [] | (:) a (list a)
map f [] = []map f (x:xs)=(f x):(map f xs)
pi = 3.14159
\ x -> x + 1
Datatype ‘a list = [] | (op ::) of ‘a * (‘a list)
fun map f [] = … | map f (x::xs) =
val pi = 3.14159
fn x => x + 1
Differences betwe
en Haske
ll & ML
Advanced Functional Programming
Tim Sheard 6Lecture 17
Some Primitives- op +;
val it = fn:int * int -> int- op *;val it = fn:int * int -> int- op -;val it = fn:int * int -> int
- op @;val it = fn:'a list * 'a list -> 'a list- length;val it = fn:'a list -> int- op ::;val it = fn:'a * 'a list -> 'a list- hd;val it = fn:'a list -> 'a- tl;val it = fn:'a list -> 'a list
Main> :t (+)(+) :: Num a => a -> a -> aMain> :t (*)(*) :: Num a => a -> a -> aMain> :t (-)(-) :: Num a => a -> a -> a
Main> :t (++)(++) :: [a] -> [a] -> [a]
Main> :t lengthlength :: [a] -> IntMain> :t (:)(:) :: a -> [a] -> [a]Main> :t headhead :: [a] -> aMain> :t tailtail :: [a] -> [a]
Advanced Functional Programming
Tim Sheard 7Lecture 17
Differences 3
(* variable definition *)
val tree1 = Fork(Fork(Tip 1,Tip 2), Tip 3);
-- variable definition
tree1 = Fork (Fork (Tip 1) (Tip 2)) (Tip 3)
use val to declare
variables
Advanced Functional Programming
Tim Sheard 8Lecture 17
functions
fn x => x + 1
let fun f x = x + 1in f 7 end
has type in ML is :
datatype T = ...
datatype ('a,'b) T = ...= (in MetaML only) 'a ('b T)
\ x -> x + 1
let f x = x + 1in f 7
has type in Haskell is ::
data T = ...
data T b a = ...
Advanced Functional Programming
Tim Sheard 9Lecture 17
case expressions
case x of [] => 5| (x::xs) => len xs + m3
case x of [] -> 5 (x:xs) -> len xs + m3
Advanced Functional Programming
Tim Sheard 10Lecture 17
Differences 4
(* booleans *)
- true;val it = true : bool- false;val it = false : bool
(* mutual recursion *)
fun even 0 = true | even n = odd (n-1)and odd 0 = false | odd n = even (n-1);
-- booleans
Main> :t TrueTrue :: BoolMain> :t FalseFalse :: Bool
-- mutual recursion
even 0 = Trueeven n = odd (n-1)odd 0 = Falseodd n = even (n-1)
No capitals
use of "and" must separate mutually recursive
functions
all functions are implicitly mutually recursive
Advanced Functional Programming
Tim Sheard 11Lecture 17
Intro to MetaML
Advanced Functional Programming
Tim Sheard 12Lecture 17
Controlling Evaluation Order
We want more than just the correct result!
We want to control other resources such as time and space without resorting to tricks or unnatural programming styles.
Mechanism - Control Evaluation Order
Advanced Functional Programming
Tim Sheard 13Lecture 17
Traditiona
l Approaches
Fixed evaluation order with language extensions
Lazy - Outermostadd strictness annotations
Strict - Innermostadd annotations like force and delay
Encode laziness using lambda in a strict settingdatatype 'a lazylist = lazyNil | lazyCons of 'a * (unit -> 'a lazylist);
fun count n = lazyCons(n, fn () => count (n+1))
Advanced Functional Programming
Tim Sheard 14Lecture 17
Limitations
None of these approaches allow computation under a lambda! This is sometimes just what is needed. For example:
fun power n = (fn x => if n=0 then 1 else x * (power (n-1) x))
power 2;
Advanced Functional Programming
Tim Sheard 15Lecture 17
Exampl
e reduction
(power 2) unfold the definition
(fn x => if 2=0 then 1 else x * (power (2-1) x)) perform the if, under the lambda
(fn x => x * (power (2-1) x)) unfold power again
(fn x => x * ((fn x => if 1=0 then 1 else x * (power (1-1) x)) x))use the beta rule to apply the explicit lambda to x
Advanced Functional Programming
Tim Sheard 16Lecture 17
Exampl
e (cont.
)(fn x => x * (if 1=0 then 1 else x * (power (1-1) x))) perform the if
(fn x => x * (x * (power (1-1) x))) unfold power again
(fn x => x * (x * ((fn x => if 0=0 then 1 else x * (power (0-1) x))) x)) use the beta rule to apply the explicit lambda to x
(fn x => x * (x * (if 0=0 then 1 else x * (power (0-1) x)))) perform the if
(fn x => x * (x * 1))
Advanced Functional Programming
Tim Sheard 17Lecture 17
Solution - Use richer
annotations
Brackets: < e >no reductions allowed in edelay computationif e:t then <e> : <t> (pronounced code of t)
Escape: ~ erelax the no reduction rule of brackets aboveEscape may only occur inside Bracketssplice code together to build larger code
Run: run eremove outermost bracketsforce computations which have been delayed
Advanced Functional Programming
Tim Sheard 18Lecture 17
Calculus
A calculus describes equivalences between program fragments.
The rules of a calculus can be applied in any order.
An implementation applies the rules in some fixed order.
Traditional rulesbeta – (fn x => e) v e[v/x]if – if true then x else y x – if false then x else y ydelta – 5 + 2 7
Advanced Functional Programming
Tim Sheard 19Lecture 17
Rules for code
Introduction rule for code< e >
1st elimination rule for code (escape-bracket elim)< … ~<e> … > ---> < … e … >~<e> must appear inside enclosing bracketse must be escape free<e> must be at level 0
2nd elimination rule for code (run-bracket elim)run <e> ---> eprovided e has no escapesthe whole expression is at level 0
Advanced Functional Programming
Tim Sheard 20Lecture 17
Power exampl
e revisited
(* power : int -> <int> -> <int> *)fun power n = fn x => if n=0 then <1> else < ~x * ~(power (n-1) x) >
(* ans : < int -> int > *)val ans = < fn z => ~(power 2 <z>) >;
Advanced Functional Programming
Tim Sheard 21Lecture 17
<fn z => ~ (power 2 <z>) >
<fn z => ~(if 2=0
then <1>
else < ~<z> * ~(power (2-1) <z>) >)>
<fn z => ~< ~<z> * ~(power (2-1) <z>) >>)
<fn z => ~< z * ~(power (2-1) <z>) >>)
<fn z => ~< z * ~(if 1=0 then <1> else < ~<z> * ~(power (1-1) <z>) >) >>)
Advanced Functional Programming
Tim Sheard 22Lecture 17
<fn z => ~< z * ~< ~<z> * ~(power (1-1) <z>) >>>
<fn z => ~< z * ~< z * ~(power (1-1) <z>) >>>
<fn z => ~< z * ~< z * ~(power 0 <z>) >>>
<fn z => ~< z * ~< z * ~<1> >>>
<fn z => ~< z * ~< z * 1 >>>
<fn z => ~< z * z * 1 >>
<fn z => z * z * 1>
Advanced Functional Programming
Tim Sheard 23Lecture 17
MetaML:
Meta-
programming
Meta-Programming: Programs that write programs
What Infrastructure is possible in a language designed to help support the algorithmic construction of other programs?Advantages of meta-programs
capture knowledgeefficient solutionsdesign ideas can be communicated and shared
Advanced Functional Programming
Tim Sheard 24Lecture 17
Types of meta-programs
Static generatorsGenerate code which is then “written to disk” and processed by normal compilers etc.Two kinds
Homogeneous systemsobject language = meta-
languageHeterogeneous systems
object-language different from meta-language
Run-time generatorsStaged ProgramsPrograms that generate other programs, and then execute the programs they generatedMetaML is a staged programming language which does run-time code generation
Advanced Functional Programming
Tim Sheard 25Lecture 17
MetaML & the Mustang Project
TheorySemantics of staged programs
What does it mean to have a staged program?How can we tell if a staged program computes the same result as its unstaged counterpart?Calculus of programs - Rules for reasoning
Staged type systemsA static type system keeps bad programs from executing by throwing away bad programs by refusing to compile them.A staged program admits more bad things.
E.g. using a variable before it is defined, or executing a program before its complete, for example.Staged languages need richer type systems to throw away these bad programs.
Automatic stagingpartial evaluation
Advanced Functional Programming
Tim Sheard 26Lecture 17
MetaML & the Mustang Project
ToolsStaged Languages
MetaML reflective: obj-lang = meta-lang = ML multi-stage
Heterogeneous (meta-lang=ML, obj-lang varies, 2 stage)
Advanced Functional Programming
Tim Sheard 27Lecture 17
Building
pieces of
code
-| <23>;val it = <23> : <int>
-| <length [1,2,3]>;val it = <%length [1,2,3]> :<int>
-| <23 + 5>;val it = <23 %+ 5> : <int>
-| <fn x => x + 5>;val it = <(fn a => a %+ 5)> : <int -> int>
-| <fn x => <x + 1>>;val it = <(fn a => <a %+ 1>)> : <int -> <int>>
Advanced Functional Programming
Tim Sheard 28Lecture 17
Let’s try it !
Run MetaML
Advanced Functional Programming
Tim Sheard 29Lecture 17
Composing pieces of
code
-| val x = <2>;val x = <2> : <int>
-| val y = <44 + ~x>;val y = <44 %+ 2> : <int>
-| fun pair x = <( ~x, ~x )>;val pair = Fn : ['b].<'b > -> <('b * 'b )>
-| pair <11>;val it = <(11,11)> : <(int * int)>
Advanced Functional Programming
Tim Sheard 30Lecture 17
Executing constructed code
-| val x = <34 + 2>;val x = <34 %+ 2> : ['a].<int>
-| run x;val it = 36 : int
-| fun id x = x;val id = Fn : ['a].'a -> 'a
-| val q = <1 + ~(id <2+3>) >;val q = <1 %+ 2 %+ 3> : <int>
-| run q;val it = 6 : int
Advanced Functional Programming
Tim Sheard 31Lecture 17
Multi-stage code
-| val z = <fn n => <n + 1>>;val z = <(fn a => <a %+ 1>)> : <int -> <int>>
-| val f = run z;val f = Fn : int -> <int>
-| f 12;val it = <%n %+ 1> : <int>
-| run it;val it = 13 : int
Advanced Functional Programming
Tim Sheard 32Lecture 17
The lift operator-| <4+1>;val it = <4 %+ 1> : <int>-| lift (4+1);val it = <5> : <int>
-| val z = <fn n => < ~(lift n) + 1>>;val z = <(fn a => <~(lift a) %+ 1>)> :<int -> <int>>
-| run z;val it = Fn : int -> <int>
-| it 5;val it = <5 %+ 1>: <int>
-| run it;val it = 6 : int
Advanced Functional Programming
Tim Sheard 33Lecture 17
Lift v.s. lexical capture
Lift cannot be used on functions-| lift id;Error: The term: id Non Qualified type, not liftable: ? -> ?
Lift makes code readable-| fun f x = <(x, ~(lift x))>;val f = Fn : ['b^].'b -> <('b * 'b )>-| f 3;val it = <(%x,3)> : <(int * int)>
Lexical capture is more efficient-| lift [1+3,4,8-4];val it = <4 :: (4 :: (4 :: [])))> : <int list >