Ml-Curry-4Up.Pdf

Ml-Curry-4Up.Pdf

CS 251 SpringFall 2019 2020 Principles of of Programming Programming Languages Languages Ben Wood More idioms for closures λ Ben Wood • Function composition Currying • Currying and partial application and Partial Application • Callbacks (e.g., reactive programming, later) and other tasty closure recipes • Functions as data representation (later) https://cs.wellesley.edu/~cs251/s20/ Currying and Partial Application 1 Currying and Partial Application 2 Function composition (right-to-left) Pipelines (left-to-right composition) fun compose (f,g) = fn x => f (g x) Common in functional programming. Closure “remembers” f and g : ('b -> 'c) * ('a -> 'b) -> ('a -> 'c) infix |> REPL prints something equivalent fun x |> f = f x fun sqrt_of_abs i = ML standard library provides infix operator o i |> abs |> Real.fromInt |> Math.sqrt fun sqrt_of_abs i = Math.sqrt(Real.fromInt(abs i)) fun sqrt_of_abs i = (Math.sqrt o Real.fromInt o abs) i val sqrt_of_abs = Math.sqrt o Real.fromInt o abs (F#, Microsoft's ML flavor, defines this by default) Right to left. Currying and Partial Application 3 Currying and Partial Application 4 Currying Example • Every ML function takes exactly one argument val sorted3 = fn x => fn y => fn z => z >= y andalso y >= x • Previously encoded n arguments via one n-tuple val t1 = ((sorted3 7) 9) 11 • Another way: 1. Calling (sorted3 7) returns closure #1 with: Take one argument and return a function that Code fn y => fn z => z >= y andalso y >= x takes another argument and… Environment: x ↦ 7 – Called “currying” after logician Haskell Curry 2. Calling closure #1 on 9 returns closure #2 with: Code fn z => z >= y andalso y >= x Environment: y ↦ 9, x ↦ 7 3. Calling closure #2 on 11 returns true Currying and Partial Application 6 Currying and Partial Application 7 Function application is left-associative Function definitions are sugar (again) val sorted3 = fn x => fn y => fn z => val sorted3 = fn x => fn y => fn z => z >= y andalso y >= x z >= y andalso y >= x val t1 = ((sorted3 7) 9) 11 val t1 = ((sorted3 7) 9) 11 e1 e2 e3 e4 fun f p1 p2 p3 … = e means desugars to (((e1 e2) e3) e4) fun f p1 = fn p2 => fn p3 => … => e val t1 = sorted3 7 9 11 fun sorted3 x y z = z >= y andalso y >= x Callers can just think Callees can just think “multi-argument function with spaces instead of a tuple expression” “multi-argument function with spaces instead of a tuple pattern” Does not interchange with tupled version. Does not interchange with tupled version. Currying and Partial Application 8 Currying and Partial Application 9 Final version Curried foldl fun sorted3 x y z = z >= y andalso y >= x val t1 = sorted3 7 9 11 As elegant syntactic sugar (fewer characters than tupling) for: fun foldl f acc xs = case xs of val sorted3 = fn x => fn y => fn z => [] => acc z >= y andalso y >= x | x::xs’ => foldl f (f(x,acc)) xs’ val t1 = ((sorted3 7) 9) 11 fun sum xs = foldl (fn (x,y) => x+y) 0 xs Function application is left-associative. Types are right-associative: sorted3 : int -> int -> int -> bool means sorted3 : int -> (int -> (int -> bool)) Currying and Partial Application 10 Currying and Partial Application 11 Partial Application Unnecessary function wrapping fun foldl f acc xs = fun f x = g x (* bad *) case xs of val f = g (* good *) [] => acc | x::xs’ => foldl f (f(acc,x)) xs’ (* bad *) fun sum_inferior xs = foldl (fn (x,y) => x+y) 0 xs fun sum_inferior xs = foldl (fn (x,y) => x+y) 0 xs (* good *) val sum = foldl (fn (x,y) => x+y) 0 val sum = fold (fn (x,y) => x+y) 0 (* best? *) foldl (fn (x,y) => x+y) 0 val sum = fold (op+) 0 evaluates to a closure that, when called with a list xs, evaluates the case-expression with: f bound to the result of foldl (fn (x,y) => x+y) Treat infix operator as normal function. acc bound to 0 Currying and Partial Application 12 Currying and Partial Application 13 Iterators and partial application The Value Restriction L If you use partial application to create a polymorphic function, it may fun exists predicate xs = not work due to the value restriction case xs of [] => false – Warning about “type vars not generalized” | x::xs’ => predicate x • And won’t let you call the function orelse exists predicate xs’ – This should surprise you; you did nothing wrong J val no = exists (fn x => x=7) [4,11,23] but you still must change your code. val hasZero = exists (fn x => x=0) – See the code for workarounds – Can discuss a bit more when discussing type inference For this reason, ML library functions of this form are usually curried – List.map, List.filter, List.foldl, ... Currying and Partial Application 14 Currying and Partial Application 15 More combining functions • What if you want to curry a tupled function or vice-versa? • What if a function’s arguments are in the wrong order for the partial application you want? Naturally, it is easy to write higher-order wrapper functions – And their types are neat logical formulas fun other_curry1 f = fn x => fn y => f y x fun other_curry2 f x y = f y x fun curry f x y = f (x,y) fun uncurry f (x,y) = f x y Currying and Partial Application 16.

View Full Text

Details

  • File Type
    pdf
  • Upload Time
    -
  • Content Languages
    English
  • Upload User
    Anonymous/Not logged-in
  • File Pages
    4 Page
  • File Size
    -

Download

Channel Download Status
Express Download Enable

Copyright

We respect the copyrights and intellectual property rights of all users. All uploaded documents are either original works of the uploader or authorized works of the rightful owners.

  • Not to be reproduced or distributed without explicit permission.
  • Not used for commercial purposes outside of approved use cases.
  • Not used to infringe on the rights of the original creators.
  • If you believe any content infringes your copyright, please contact us immediately.

Support

For help with questions, suggestions, or problems, please contact us