1 / 61

A Third Look At ML

A Third Look At ML. Outline. More pattern matching Function values and anonymous functions Higher-order functions and currying Predefined higher-order functions. More Pattern-Matching. Last time we saw pattern-matching in function definitions: fun f 0 = "zero" | f _ = "non-zero";

nguyet
Download Presentation

A Third Look At ML

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. A Third Look At ML Modern Programming Languages

  2. Outline • More pattern matching • Function values and anonymous functions • Higher-order functions and currying • Predefined higher-order functions Modern Programming Languages

  3. More Pattern-Matching • Last time we saw pattern-matching in function definitions: • fun f 0 = "zero"| f _ = "non-zero"; • Pattern-matching occurs in several other kinds of ML expressions: • case n of 0 => "zero" | _ => "non-zero"; Modern Programming Languages

  4. Match Syntax • A rule is a piece of ML syntax that looks like this: • A match consists of one or more rules separated by a vertical bar, like this: • Each rule in a match must have the same type of expression on the right-hand side • A match is not an expression by itself, but forms a part of several kinds of ML expressions <rule> ::= <pattern> => <expression> <match> ::= <rule> | <rule> '|' <match> Modern Programming Languages

  5. Case Expressions • The syntax is • This is a very powerful case construct—unlike many languages, it does more than just compare with constants - case 1+1 of = 3 => "three" | = 2 => "two" | = _ => "hmm"; val it = "two" : string <case-expr> ::= case <expression> of <match> Modern Programming Languages

  6. Example case x of _::_::c::_ => c | _::b::_ => b | a::_ => a | nil => 0 The value of this expression is the third elementof the list x, if it has at least three, or the second element if x has only two, or the first element if x has only one, or 0 if x is empty. Modern Programming Languages

  7. Generalizes if • The two expressions above are equivalent • So if-then-else is really just a special case of case if exp1 then exp2 else exp3 case exp1 of true => exp2 | false => exp3 Modern Programming Languages

  8. Behind the Scenes • Expressions using if are actually treated as abbreviations for case expressions • This explains some odd SML/NJ error messages: - if 1=1 then 1 else 1.0; Error: types of rules don't agree [literal] earlier rule(s): bool -> int this rule: bool -> real in rule: false => 1.0 Modern Programming Languages

  9. Outline • More pattern matching • Function values and anonymous functions • Higher-order functions and currying • Predefined higher-order functions Modern Programming Languages

  10. Predefined Functions • When an ML language system starts, there are many predefined variables • Some are bound to functions: - ord;val it = fn : char -> int- ~;val it = fn : int -> int Modern Programming Languages

  11. Defining Functions • We have seen the fun notation for defining new named functions • You can also define new names for old functions, using val just as for other kinds of values: - val x = ~;val x = fn : int -> int- x 3;val it = ~3 : int Modern Programming Languages

  12. Function Values • Functions in ML do not have names • Just like other kinds of values, function values bound to one or more variables • The fun syntax does two separate things: • Creates a new function entity • Binds that function entity to a name Modern Programming Languages

  13. Anonymous Functions • Named function: • Anonymous function: - fun f x = x + 2;val f = fn : int -> int- f 1;val it = 3 : int - fn x => x + 2;val it = fn : int -> int- (fn x => x + 2) 1;val it = 3 : int Modern Programming Languages

  14. The fn Syntax • Another use of the match syntax • Using fn, we get an expression whose value is an (anonymous) function • We can define what fun does in terms of val and fn • These two definitions have the same effect: • fun f x = x + 2 • val f = fn x => x + 2 <fun-expr> ::= fn <match> a match Modern Programming Languages

  15. fn and rec • A special syntax is required for binding variables to recursive functions with fn • Since the expression is evaluated before binding, and the function has no name, you can’t recurse directly • The rec keyword allows you to accomplish this- val rec fact = fn 0 => 1 | n => n*fact(n-1);val fact = fn : int -> int Modern Programming Languages

  16. Using Anonymous Functions • One simple application: when you need a small function in just one place • Without fn: • With fn: - fun intBefore (a,b) = a < b;val intBefore = fn : int * int -> bool- quicksort ([1,4,3,2,5], intBefore);val it = [1,2,3,4,5] : int list - quicksort ([1,4,3,2,5], fn (a,b) => a<b);val it = [1,2,3,4,5] : int list- quicksort ([1,4,3,2,5], fn (a,b) => a>b);val it = [5,4,3,2,1] : int list Modern Programming Languages

  17. The op keyword • Binary operators are special functions • Sometimes you want to treat them like plain functions: to pass <, for example, as an argument of type int * int -> bool • The keyword op before an operator gives you the underlying function - op *; val it = fn : int * int -> int - quicksort ([1,4,3,2,5], op <); val it = [1,2,3,4,5] : int list Modern Programming Languages

  18. Defining Binary Infix “Operators” • You can define functions that use infix notation: • x f y • Use the infix statement: • infix [precedence: 0,3-7] <fun-name> • Define with infix syntax: • fun x f y = … Modern Programming Languages

  19. Defining an Infix plus Function - infix 6 plus; infix 6 plus - fun x plus y = x + y; val plus = fn : int * int -> int - 2 plus 3; val it = 5 : int - val g = op plus; val g = fn : int * int -> int - g (2,3); val it = 5 : int Modern Programming Languages

  20. Operator Precedence • 7: * / div mod • 6: + - ^ • 5: :: @ • 4: = <> < > <= >= • Don’t worry about the others Modern Programming Languages

  21. fn vs. case The notation case exp of pat1 => exp1 | ... | patn => expn is shorthand for the function application (fn pat1 => exp1 | ... | patn => expn) exp Modern Programming Languages

  22. Function Objects in C++ Modern Programming Languages

  23. Anonymous Functions in C++0x Modern Programming Languages

  24. Outline • More pattern matching • Function values and anonymous functions • Higher-order functions and currying • Predefined higher-order functions Modern Programming Languages

  25. Higher-order Functions • Every function has an order: • A function that does not take any functions as parameters, and does not return a function value, has order 1 • A function that takes a function as a parameter or returns a function value has order n+1, where n is the order of its highest-order function parameter or returned value • The quicksort we just saw is a second-order function Modern Programming Languages

  26. Practice What is the order of functions with each of the following ML types? int * int -> bool int list * (int * int -> bool) -> int list int -> int -> int (int -> int) * (int -> int) -> (int -> int) int -> bool -> real -> string What can you say about the order of a function with this type? ('a -> 'b) * ('c -> 'a) -> 'c -> 'b Modern Programming Languages

  27. Currying • We've seen how to get two parameters into a function by passing a 2-tuple:fun f (a,b) = a + b; • Another way is to write a function that takes the first argument, and returns another function that takes the second argument:fun g a = fn b => a+b; • The general name for this is currying • Arguments are supplied in successive calls Modern Programming Languages

  28. Curried Addition - fun f (a,b) = a+b; (* not curried *) val f = fn : int * int -> int - fun g a = fn b => a+b; (* curried *) val g = fn : int -> int -> int - f(2,3); val it = 5 : int - g 2 3; val it = 5 : int • Remember that function application is left-associative • So g 2 3 means ((g 2) 3) Modern Programming Languages

  29. Advantages • No tuples: we get to write g 2 3 instead of f(2,3) • But the real advantage: we get to specialize functions for particular initial parameters - val add2 = g 2;val add2 = fn : int -> int- add2 3;val it = 5 : int- add2 10;val it = 12 : int Modern Programming Languages

  30. Advantages: Example • Like the previous quicksort • But now, the comparison function is the first, curried parameter - quicksort (op <) [1,4,3,2,5];val it = [1,2,3,4,5] : int list - val sortBackward = quicksort (op >);val sortBackward = fn : int list -> int list- sortBackward [1,4,3,2,5];val it = [5,4,3,2,1] : int list Modern Programming Languages

  31. Multiple Curried Parameters • Currying generalizes to any number of parameters - fun f (a,b,c) = a+b+c;val f = fn : int * int * int -> int- fun g a = fn b => fn c => a+b+c;val g = fn : int -> int -> int -> int- f (1,2,3);val it = 6 : int- g 1 2 3;val it = 6 : int Modern Programming Languages

  32. Notation For Currying • There is a much simpler notation for currying (on the next slide) • The long notation we have used so far makes the little intermediate anonymous functions explicit: • But as long as you understand how it works, the simpler notation is much easier to read and write… fun g a = fn b => fn c => a+b+c; Modern Programming Languages

  33. Easier Notation for Currying • Instead of writing:fun f a = fn b => a+b; • We can just write:fun f a b = a+b; • This generalizes for any number of curried arguments - fun f a b c d = a+b+c+d; val f = fn : int -> int -> int -> int -> int Modern Programming Languages

  34. std::bind in C++0x Modern Programming Languages

  35. Function Composition • Used frequently in mathematics and functional programming • Define a new function as a sequential application of one or more other functions: • h(x) = f(g(x)) • Can be written h = f ੦ g • ML uses the letter ‘o’ • g’s output type must be f’s input type Modern Programming Languages

  36. Function CompositionExample 1 - fun f x = x + 2; val f = fn : int -> int - fun g x = 3 * x; val g = fn : int -> int - fun h x = f (g x); val h = fn : int -> int - f (g 5); val it = 17 : int - h 5; val it = 17 : int Modern Programming Languages

  37. Function CompositionExample 2 - f o g; val it = fn : int -> int -val h = f o g; (* or fun h x = (f o g) x; *) val h = fn : int -> int - h 5; val it = 17 : int Modern Programming Languages

  38. Function CompositionExample 3 - map; val it = fn : ('a -> 'b) -> 'a list -> 'b list - map ord; val it = fn : char list -> int list - explode; val it = fn : string -> char list - val a = map ord o explode; val a = fn : string -> int list - a "hello"; val it = [104,101,108,108,111] : int list Modern Programming Languages

  39. Strange Behavior - hd; val it = fn : 'a list -> 'a - tl; val it = fn : 'a list -> 'a list - hd o tl; stdIn:32.1-32.8 Warning: type vars not generalized because of value restriction are instantiated to dummy types (X1,X2,...) val it = fn : ?.X1 list -> ?.X1 Modern Programming Languages

  40. ML’s Value Restriction • A “dark corner” • Has to do with making imperative programming as safe as possible in ML • Expressions at the “top level” must be one of the following: • values (no further evaluation pending) • 5 is a value; 2+3 is not • monomorphic (no type variables) • See next slide Modern Programming Languages

  41. Value Restriction Workaround - val f = map length; stdIn:18.5-18.19 Warning: type vars not generalized because of value restriction are instantiated to dummy types (X1,X2,...) val f = fn : ?.X1 list list -> int list - fun f x = map length x; (*In f: not at top-level *) val f = fn : 'a list list -> int list - f [[1,2],[3,4,5]]; val it = [2,3] : int list - f nil; val it = [] : int list Modern Programming Languages

  42. Outline • More pattern matching • Function values and anonymous functions • Higher-order functions and currying • Predefined higher-order functions Modern Programming Languages

  43. Predefined Higher-Order Functions • We will use three important predefined higher-order functions: • map • foldr • foldl • Actually, foldr and foldl are very similar, as you might guess from the names Modern Programming Languages

  44. The map Function • Used to apply a function to every element of a list, and collect a list of results - map ~ [1,2,3,4];val it = [~1,~2,~3,~4] : int list - map (fn x => x+1) [1,2,3,4];val it = [2,3,4,5] : int list- map (fn x => x mod 2 = 0) [1,2,3,4];val it = [false,true,false,true] : bool list- map (op +) [(1,2),(3,4),(5,6)];val it = [3,7,11] : int list Modern Programming Languages

  45. The map Function Is Curried - map; val it = fn : ('a -> 'b) -> 'a list -> 'b list - val f = map (op +);val f = fn : (int * int) list -> int list- f [(1,2),(3,4)];val it = [3,7] : int list Modern Programming Languages

  46. Implementing map - fun map f nil = nil = | map f (x::xs) = f x :: map f xs; val map = fn : ('a -> 'b) -> 'a list -> 'b list - map (op ~) [1,2,3]; val it = [~1,~2,~3] : int list Modern Programming Languages

  47. Implementing mapTake 2 - fun map f nil = nil = | map f (x::xs) = (op ::)(f x, map f xs); val map = fn : ('a -> 'b) -> 'a list -> 'b list - map (op ~) [1,2,3]; val it = [~1,~2,~3] : int list Modern Programming Languages

  48. A Common Pattern • Consider adding list elements:fun sum nil = 0| sum (h::t) = (op +)(h,sum t);computes x1 + (x2 + (x3 +… (xn + 0)…)) • Or multiplying them:fun mult nil = 1| mult(h::t) = (op * )(h,mult t);computes x1 * (x2 * (x3 *… (xn * 1)…)) • The pattern:fun newf nil = c| newf(h::t) = f(h,newf t);computes f(x1,f(x2,f(x3,… f(xn,c)…)) Modern Programming Languages

  49. Capturing the PatternSee reduce.sml • Make the accumulatingfunction (f) and the initial value (c) parameters to a higher-order function • We’ll call the generic function reduce: fun reduce _ c nil = c | reduce f c (h::t) = f(h, reduce f c t); Modern Programming Languages

  50. What reduce ComputesA Derivation • Then reduce f c [x1,x2,…,xn] =f(x1,reduce f c [x2,…,xn]) =f(x1,f(x2,reduce f c [x3,…,xn])) … =f(x1,f(x2, …f(xn,reduce f c nil) …)) • f(x1,f(x2, …f(xn,c) …)) • Note that this evaluates inside-out • So the last list element is processed first • And the “first shall be last” :-) • See also: reduce.py Modern Programming Languages

More Related