Com S 541 Lecture -*- Outline -*- * Procedural Abstraction (Higher-Order Programming, 3.6) def: the *order* of a function with no function arguments is 1, the order of a function with function arguments is 1 plus the maximum of the order of its arguments def: A function is *higher-order* if its order is greater than 1. ** Basic Operations (3.6.1) Q: What are the 4 basic operations of higher-order programming? procedural abstraction: converting statements to procedures (Liskov & Guttag's abstraction by parameterization) genericity: passing procedures as arguments (abstracting out statements, not just data) instantiation: returning procedure values as results (creation of new procedures) embedding: putting procedures in data structures *** procedural abstraction Q: How can you make a statement S into a procedure? proc {$} S end this freezes execution (makes a closure, or thunk) Q: Suppose we want two variants of a procedure that are similar, but differ a bit? Use arguments *** genericity Q: What's an example of passing function arguments we've seen already? Iterate **** for lists ------------------------------------------ ABSTRACTING A COMMON PATTERN fun {Sum Ls} case Ls of E|Es then E+{Sum Es} else 0 end end fun {Product Ls} case Ls of E|Es then E*{Product Es} else 1 end end ------------------------------------------ Q: What are the parts specific to computing the sum? the product? identify the common parts of this pattern, and pass the changing parts as arguments fun {FoldR Ls Op Z} case Ls of E|Es then {Op E {FoldR Es Op Z Es}} else Z end end Q: How can you write Sum and Product using FoldR? fun {Sum Ls} {FoldR Ls fun {$ X Y} X+Y end 0} end fun {Product Ls} {FoldR Ls Number.'*' 0} end Q: What is {FoldR (1|(2|(3|nil))) Number.'-' 0}? (1 - (2 - (3 - 0))) Note how this is a homomorphism, translating | to - and nil to 0. in general X1 | X2 | ... | Xn | nil = '|'(X1'|'(X2'|'( ... '|'(Xn nil)...))) is transformed into {F X1 {F X2 { ... {F Xn U} ...}}} ------------------------------------------ FOR YOU TO DO Using FoldR, write: Sum Product Concat: >}: > All: }: Boolean> ------------------------------------------ The book also does a generic version of MergeSort. **** for trees Compare to section 3.6.4.2 ------------------------------------------ FOR YOU TO DO ::= leaf | tree ( key: value: T left: right: ) Generalize: fun {Preorder T} case T of leaf then nil [] tree(key: L value: V left: Left right: Right) then (L#V) | {Append {Preorder Left} {Preorder Right}} end end fun {IncTree T} case T of leaf then leaf [] tree(key: L value: V left: Left right: Right) then tree(key: L value: 1+V left: {IncTree Left} right: {IncTree Right}) end end ------------------------------------------ To make the pattern clearer, rewrite the above as: fun {Preorder T} case T of leaf then nil [] tree(key: L value: V left: Left right: Right) then fun {Combine L V Left Right} (L#V) | {Append Left Right} end in {Combine L V {Preorder Left} {Preorder Right}} end end fun {IncTree T} case T of leaf then leaf [] tree(key: L value: V left: Left right: Right) then fun {Combine L V Left Right} tree(key: L value: 1+V left: Left right: Right) end in {Combine L V {IncTree Left} {IncTree Right}} end end Then identify the changing parts, and make those parameters: fun {FoldTree T Combine Base} case T of leaf then Base [] tree(key: L value: V left: Left right: Right) then {Combine L V {FoldTree Left Combine Base} {FoldTree Right Combine Base}} end end Q: How would you use the result to write Preorder and IncTree? fun {Preorder T} {FoldTree fun {$ L V Left Right} (L#V) | {Append Left Right} end nil T} end fun {IncTree T} {FoldTree fun {$ L V Left Right} tree(key: L value: 1+V left: Left right: Right) end leaf T} end *** instantiation (currying) (3.6.6) ------------------------------------------ INSTANTIATION or RULES TO PRODUCE RULES Aka: factories, generators, curried functions Examples: fun {MakeSort Comparison} fun {$ Ls} {Sort Ls Comparison} end end Use of MakeSort: SortGT = {MakeSort fun {$ X Y} X > Y end} {SortGT List1} {SortGT List2} ... ------------------------------------------ Planning in advance to make functions is currying. The following shows that in Physics, this is also useful... Q: How could we do this for the tree example? fun {FoldTree Combine Base} fun {$ T} case T of leaf then Base [] tree(key: L value: V left: Left right: Right) then {Combine L V {FoldTree Combine Base Left} {FoldTree Combine Base Right}} end end end Q: How would you use the that to write Preorder and IncTree? Preorder = {FoldTree fun {$ L V Left Right} (L#V) | {Append Left Right} end nil} IncTree = {FoldTree fun {$ L V Left Right} tree(key: L value: 1+V left: Left right: Right) end leaf} ------------------------------------------ GRAVITATIONAL FORCE FIELDS AS CURRIED FUNCTIONS declare G = 6.670e~11 %% UNITS: N * m^2 / kg^2 fun {Square R} %% UNITS: m -> m^2 R*R end fun {GravForce M1 R M2} %% UNITS: kg x m x kg -> N if R == 0.0 then 0.0 else G * M1 * M2 / {Square R} end end fun {GravField M1} %% UNITS: kg -> m -> kg -> N fun {$ R} fun {$ M2} if R == 0.0 then 0.0 else G * M1 * M2 / {Square R} end end end end %%% USING IT MassOfEarth = 5.96e24 %% UNITS: kg RadiusOfEarth = 6.37e6 %% UNITS: m EarthsField %% UNITS: m -> kg -> N = {GravField MassOfEarth} ForceAtSurface %% UNITS: kg -> N = {EarthsField RadiusOfEarth} ------------------------------------------ ------------------------------------------ FOR YOU TO DO Write procedure Compose such that: {{Compose Head Tail} [a b c]} = b = {Head [b c]} = {Head {Tail [a b c]}} {{Compose Not IsEmpty} nil} = false = {Not {IsEmpty nil}} (The examples assume that: fun {Head L} X|_=L in X end fun {Tail L} _|Xs=L in Xs end fun {IsEmpty L} L == null end ) ------------------------------------------ Show how to generalize these examples to get the answer. *** embedding ------------------------------------------ EMBEDDING Putting closures in data is useful for: - explicit lazy evaluation - modules = records of operations - components, which return modules - manipulating actions as data (e.g., in testing) ------------------------------------------ This is also the basic OO paradigm ** Loop abstractions (3.6.2-3) (skip) *** FoldL and other loops over lists (3.6.2) These are examples of genericity Q: Does it do any good in the declarative model to run an action {A I} for each integer I in 1 to 10? No FoldL accumulates from the left, so is opposite of homomorphic, vs. FoldR *** linguistic support for loops (3.6.3) Q: What's the difference between a declarative and imperative loop? In the declarative version, each run of the body has a different (new) varaible ------------------------------------------ FOR LOOPS IN OZ Simple counting: for I in A .. B do {Stmt I} end With step S: for I in A .. B; S do {Stmt I} end For lists: for X in L do {Stmt X} end With pattern match: for foo(X Y) in L do {Stmt X Y} end Collection of results: for foo(X Y) in L collect: C do {Stmt X Y C} end ------------------------------------------ See documentation for more Q: How would you precisely define these? By desugaring to calls of procedures