CSE341: Programming Languages Lecture 9 Function-Closure Idioms. Dan Grossman Winter 2013

Similar documents
CSE341: Programming Languages Lecture 9 Function-Closure Idioms. Dan Grossman Fall 2011

CSci 4223 Principles of Programming Languages

CSE341: Programming Languages Winter 2018 Unit 3 Summary Zach Tatlock, University of Washington

CSE 341 Section 5. Winter 2018

Programming Languages. Function-Closure Idioms. Adapted from Dan Grossman's PL class, U. of Washington

CSE341 Section 3. Standard-Library Docs, First-Class Functions, & More

CSE341: Programming Languages Lecture 7 First-Class Functions. Dan Grossman Winter 2013

CSE341: Programming Languages Lecture 11 Type Inference. Dan Grossman Spring 2016

CSE 341 : Programming Languages

CSE341: Programming Languages Lecture 4 Records, Datatypes, Case Expressions. Dan Grossman Spring 2013

CS Lecture 6: Map and Fold. Prof. Clarkson Spring Today s music: Selections from the soundtrack to 2001: A Space Odyssey

CSE 413 Languages & Implementation. Hal Perkins Winter 2019 Structs, Implementing Languages (credits: Dan Grossman, CSE 341)

CSCI-GA Scripting Languages

CSE 505: Concepts of Programming Languages

CSE 341: Programming Languages

CS Lecture 6: Map and Fold. Prof. Clarkson Fall Today s music: Selections from the soundtrack to 2001: A Space Odyssey

CSE413: Programming Languages and Implementation Racket structs Implementing languages with interpreters Implementing closures

CS 360 Programming Languages Day 14 Closure Idioms

CSE341 Spring 2016, Midterm Examination April 29, 2016

CSE341: Programming Languages Lecture 19 Introduction to Ruby and OOP. Dan Grossman Winter 2013

CMSC 330: Organization of Programming Languages. Functional Programming with Lists

CMSC 330: Organization of Programming Languages. Functional Programming with Lists

CSE341, Fall 2011, Midterm Examination October 31, 2011

Tuples. CMSC 330: Organization of Programming Languages. Examples With Tuples. Another Example

CSE341: Programming Languages Lecture 25 Subtyping for OOP; Comparing/Combining Generics and Subtyping. Dan Grossman Winter 2013

CSE341 Spring 2016, Midterm Examination April 29, 2016

Ruby logistics. CSE341: Programming Languages Lecture 19 Introduction to Ruby and OOP. Ruby: Not our focus. Ruby: Our focus. A note on the homework

CMSC330. Objects, Functional Programming, and lambda calculus

Closures. Mooly Sagiv. Michael Clarkson, Cornell CS 3110 Data Structures and Functional Programming

CSE341, Spring 2013, Final Examination June 13, 2013

CSE 341: Programming Languages

CS152: Programming Languages. Lecture 11 STLC Extensions and Related Topics. Dan Grossman Spring 2011

CSE3322 Programming Languages and Implementation

Datatype declarations

Part I: Written Problems

Higher-Order Functions

Exercises on ML. Programming Languages. Chanseok Oh

Programovací jazyky F# a OCaml. Chapter 5. Hiding recursion using function-as-values

Closures. Mooly Sagiv. Michael Clarkson, Cornell CS 3110 Data Structures and Functional Programming

CSE 341: Programming Languages

CSE341, Fall 2011, Midterm Examination October 31, 2011

CSE 341, Autumn 2015, Ruby Introduction Summary

SNU Programming Language Theory

Variables and Bindings

Redefinition of an identifier is OK, but this is redefinition not assignment; Thus

Introduction to OCaml

CSE341: Programming Languages Lecture 6 Nested Patterns Exceptions Tail Recursion. Zach Tatlock Winter 2018

Recap: Functions as first-class values

Lectures 24 and 25: Scheduling; Introduction to Effects

CSE341 Autumn 2017, Midterm Examination October 30, 2017

UMBC CMSC 331 Final Exam

CS 360: Programming Languages Lecture 12: More Haskell

CS152: Programming Languages. Lecture 7 Lambda Calculus. Dan Grossman Spring 2011

Notes from a Short Introductory Lecture on Scala (Based on Programming in Scala, 2nd Ed.)

CMSC 330: Organization of Programming Languages. OCaml Imperative Programming

CS-XXX: Graduate Programming Languages. Lecture 9 Simply Typed Lambda Calculus. Dan Grossman 2012

Standard ML. Curried Functions. ML Curried Functions.1

Lists. Michael P. Fourman. February 2, 2010

L3 Programming September 19, OCaml Cheatsheet

Announcements. CSCI 334: Principles of Programming Languages. Lecture 5: Fundamentals III & ML

OCaml. ML Flow. Complex types: Lists. Complex types: Lists. The PL for the discerning hacker. All elements must have same type.

CMSC 330: Organization of Programming Languages. OCaml Imperative Programming

Whereweare. CS-XXX: Graduate Programming Languages. Lecture 7 Lambda Calculus. Adding data structures. Data + Code. What about functions

CSE341: Programming Languages Lecture 17 Implementing Languages Including Closures. Dan Grossman Autumn 2018

Recap. Recap. If-then-else expressions. If-then-else expressions. If-then-else expressions. If-then-else expressions

Review. CS152: Programming Languages. Lecture 11 STLC Extensions and Related Topics. Let bindings (CBV) Adding Stuff. Booleans and Conditionals

Using Scala for building DSL s

A Functional Evaluation Model

CS558 Programming Languages

The Haskell HOP: Higher-order Programming

CSE 130: Programming Languages. Polymorphism. Ranjit Jhala UC San Diego

CS558 Programming Languages

CPL 2016, week 10. Clojure functional core. Oleg Batrashev. April 11, Institute of Computer Science, Tartu, Estonia

News. CSE 130: Programming Languages. Environments & Closures. Functions are first-class values. Recap: Functions as first-class values

Begin at the beginning

Chapter 3 Linear Structures: Lists

Programming Languages and Compilers (CS 421)

1. true / false By a compiler we mean a program that translates to code that will run natively on some machine.

We have written lots of code so far It has all been inside of the main() method What about a big program? The main() method is going to get really

A quick introduction to SML

CS Lecture 5: Pattern Matching. Prof. Clarkson Fall Today s music: Puff, the Magic Dragon by Peter, Paul & Mary

Smalltalk: developed at Xerox Palo Alto Research Center by the Learning Research Group in the 1970 s (Smalltalk-72, Smalltalk-76, Smalltalk-80)

CS 11 Haskell track: lecture 1

INF 212 ANALYSIS OF PROG. LANGS FUNCTION COMPOSITION. Instructors: Crista Lopes Copyright Instructors.

CSC324- TUTORIAL 5. Shems Saleh* *Some slides inspired by/based on Afsaneh Fazly s slides

Racket. CSE341: Programming Languages Lecture 14 Introduction to Racket. Getting started. Racket vs. Scheme. Example.

List Functions, and Higher-Order Functions

To figure this out we need a more precise understanding of how ML works

CSE 130 [Winter 2014] Programming Languages

CSc 372 Comparative Programming Languages

Chapter 3 Linear Structures: Lists

CSE341 Spring 2017, Midterm Examination April 28, 2017

CSE341 Autumn 2017, Final Examination December 12, 2017

02157 Functional Programming. Michael R. Ha. Lecture 2: Functions, Types and Lists. Michael R. Hansen

Harvard School of Engineering and Applied Sciences CS 152: Programming Languages

# true;; - : bool = true. # false;; - : bool = false 9/10/ // = {s (5, "hi", 3.2), c 4, a 1, b 5} 9/10/2017 4

Haske k ll An introduction to Functional functional programming using Haskell Purely Lazy Example: QuickSort in Java Example: QuickSort in Haskell

Programming Languages

Handout 2 August 25, 2008

Next: What s in a name? Programming Languages. Data model in functional PL. What s in a name? CSE 130 : Fall Lecture 13: What s in a Name?

Transcription:

CSE341: Programming Languages Lecture 9 Function-Closure Idioms Dan Grossman Winter 2013

More idioms We know the rule for lexical scope and function closures Now what is it good for A partial but wide-ranging list: Pass functions with private data to iterators: Done Combine functions (e.g., composition) Currying (multi-arg functions and partial application) Callbacks (e.g., in reactive programming) Implementing an ADT with a record of functions (optional) 2

Combine functions Canonical example is function composition: fun compose (f,g) = fn x => f (g x) Creates a closure that remembers what f and g are bound to Type ('b -> 'c) * ('a -> 'b) -> ('a -> 'c) but the REPL prints something equivalent ML standard library provides this as infix operator o Example (third version best): 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 3

Left-to-right or right-to-left val sqrt_of_abs = Math.sqrt o Real.fromInt o abs As in math, function composition is right to left take absolute value, convert to real, and take square root square root of the conversion to real of absolute value Pipelines of functions are common in functional programming and many programmers prefer left-to-right Can define our own infix operator This one is very popular (and predefined) in F# infix > fun x > f = f x fun sqrt_of_abs i = i > abs > Real.fromInt > Math.sqrt 4

Another example Backup function fun backup1 (f,g) = fn x => case f x of NONE => g x SOME y => y As is often the case with higher-order functions, the types hint at what the function does ('a -> 'b option) * ('a -> 'b) -> 'a -> 'b 5

More idioms We know the rule for lexical scope and function closures Now what is it good for A partial but wide-ranging list: Pass functions with private data to iterators: Done Combine functions (e.g., composition) Currying (multi-arg functions and partial application) Callbacks (e.g., in reactive programming) Implementing an ADT with a record of functions (optional) 6

Currying Recall every ML function takes exactly one argument Previously encoded n arguments via one n-tuple Another way: Take one argument and return a function that takes another argument and Called currying after famous logician Haskell Curry 7

Example val sorted3 = fn x => fn y => fn z => z >= y andalso y >= x val t1 = ((sorted3 7) 9) 11 Calling (sorted3 7) returns a closure with: Code fn y => fn z => z >= y andalso y >= x Environment maps x to 7 Calling that closure with 9 returns a closure with: Code fn z => z >= y andalso y >= x Environment maps x to 7, y to 9 Calling that closure with 11 returns true 8

Syntactic sugar, part 1 val sorted3 = fn x => fn y => fn z => z >= y andalso y >= x val t1 = ((sorted3 7) 9) 11 In general, e1 e2 e3 e4, means ( ((e1 e2) e3) e4) So instead of ((sorted3 7) 9) 11, can just write sorted3 7 9 11 Callers can just think multi-argument function with spaces instead of a tuple expression Different than tupling; caller and callee must use same technique 9

Syntactic sugar, part 2 val sorted3 = fn x => fn y => fn z => z >= y andalso y >= x val t1 = ((sorted3 7) 9) 11 In general, fun f p1 p2 p3 = e, means fun f p1 = fn p2 => fn p3 => => e So instead of val sorted3 = fn x => fn y => fn z => or fun sorted3 x = fn y => fn z =>, can just write fun sorted3 x y z = x >=y andalso y >= x Callees can just think multi-argument function with spaces instead of a tuple pattern Different than tupling; caller and callee must use same technique 10

Final version fun sorted3 x y z = z >= y andalso y >= x val t1 = sorted3 7 9 11 As elegant syntactic sugar (even fewer characters than tupling) for: val sorted3 = fn x => fn y => fn z => z >= y andalso y >= x val t1 = ((sorted3 7) 9) 11 11

Curried fold A more useful example and a call too it Will improve call next fun fold f acc xs = case xs of [] => acc x::xs => fold f (f(acc,x)) xs fun sum xs = fold (fn (x,y) => x+y) 0 xs 12

Too Few Arguments Previously used currying to simulate multiple arguments But if caller provides too few arguments, we get back a closure waiting for the remaining arguments Called partial application Convenient and useful Can be done with any curried function No new semantics here: a pleasant idiom 13

Example fun fold f acc xs = case xs of [] => acc x::xs => fold f (f(acc,x)) xs fun sum_inferior xs = fold (fn (x,y) => x+y) 0 xs val sum = fold (fn (x,y) => x+y) 0 As we already know, fold (fn (x,y) => x+y) 0 evaluates to a closure that given xs, evaluates the case-expression with f bound to fold (fn (x,y) => x+y) and acc bound to 0 14

Unnecessary function wrapping fun sum_inferior xs = fold (fn (x,y) => x+y) 0 xs val sum = fold (fn (x,y) => x+y) 0 Previously learned not to write fun f x = g x when we can write val f = g This is the same thing, with fold (fn (x,y) => x+y) 0 in place of g 15

Iterators Partial application is particularly nice for iterator-like functions Example: fun exists predicate xs = case xs of [] => false x::xs => predicate xs orelse exists predicate xs val no = exists (fn x => x=7) [4,11,23] val haszero = exists (fn x => x=0) For this reason, ML library functions of this form usually curried Examples: List.map, List.filter, List.foldl 16

The Value Restriction Appears If you use partial application to create a polymorphic function, it may not work due to the value restriction Warning about type vars not generalized And won t let you call the function This should surprise you; you did nothing wrong but you still must change your code See the code for workarounds Can discuss a bit more when discussing type inference 17

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 18

Efficiency So which is faster: tupling or currying multiple-arguments? They are both constant-time operations, so it doesn t matter in most of your code plenty fast Don t program against an implementation until it matters! For the small (zero?) part where efficiency matters: It turns out SML/NJ compiles tuples more efficiently But many other functional-language implementations do better with currying (OCaml, F#, Haskell) So currying is the normal thing and programmers read t1 -> t2 -> t3 -> t4 as a 3-argument function that also allows partial application 19

More idioms We know the rule for lexical scope and function closures Now what is it good for A partial but wide-ranging list: Pass functions with private data to iterators: Done Combine functions (e.g., composition) Currying (multi-arg functions and partial application) Callbacks (e.g., in reactive programming) Implementing an ADT with a record of functions (optional) 20

ML has (separate) mutation Mutable data structures are okay in some situations When update to state of world is appropriate model But want most language constructs truly immutable ML does this with a separate construct: references Introducing now because will use them for next closure idiom Do not use references on your homework You need practice with mutation-free programming They will lead to less elegant solutions 21

References New types: t ref where t is a type New expressions: ref e to create a reference with initial contents e e1 := e2 to update contents!e to retrieve contents (not negation) 22

References example val x = ref 42 val y = ref 42 val z = x val _ = x := 43 val w = (!y) + (!z) (* 85 *) (* x + 1 does not type-check *) x z y A variable bound to a reference (e.g., x) is still immutable: it will always refer to the same reference But the contents of the reference may change via := And there may be aliases to the reference, which matter a lot References are first-class values Like a one-field mutable object, so := and! don t specify the field 23

Callbacks A common idiom: Library takes functions to apply later, when an event occurs examples: When a key is pressed, mouse moves, data arrives When the program enters some state (e.g., turns in a game) A library may accept multiple callbacks Different callbacks may need different private data with different types Fortunately, a function s type does not include the types of bindings in its environment (In OOP, objects and private fields are used similarly, e.g., Java Swing s event-listeners) 24

Mutable state While it s not absolutely necessary, mutable state is reasonably appropriate here We really do want the callbacks registered to change when a function to register a callback is called 25

Example call-back library Library maintains mutable state for what callbacks are there and provides a function for accepting new ones A real library would all support removing them, etc. In example, callbacks have type int->unit So the entire public library interface would be the function for registering new callbacks: val onkeyevent : (int -> unit) -> unit (Because callbacks are executed for side-effect, they may also need mutable state) 26

Library implementation val cbs : (int -> unit) list ref = ref [] fun onkeyevent f = cbs := f :: (!cbs) fun onevent i = let fun loop fs = case fs of [] => () f::fs => (f i; loop fs ) in loop (!cbs) end 27

Clients Can only register an int -> unit, so if any other data is needed, must be in closure s environment And if need to remember something, need mutable state Examples: val timespressed = ref 0 val _ = onkeyevent (fn _ => timespressed := (!timespressed) + 1) fun printifpressed i = onkeyevent (fn _ => if i=j then print ("pressed " ^ Int.toString i) else ()) 28

More idioms We know the rule for lexical scope and function closures Now what is it good for A partial but wide-ranging list: Pass functions with private data to iterators: Done Combine functions (e.g., composition) Currying (multi-arg functions and partial application) Callbacks (e.g., in reactive programming) Implementing an ADT with a record of functions (optional) 29

Optional: Implementing an ADT As our last idiom, closures can implement abstract data types Can put multiple functions in a record The functions can share the same private data Private data can be mutable or immutable Feels a lot like objects, emphasizing that OOP and functional programming have some deep similarities See code for an implementation of immutable integer sets with operations insert, member, and size The actual code is advanced/clever/tricky, but has no new features Combines lexical scope, datatypes, records, closures, etc. Client use is not so tricky 30