<<

4/4/2016

Curried Functions

is a tech- Curry nique that takes a function of N arguments and produces a related one where some of the arguments are fixed • In Scheme – (define add1 (curry + 1)) – (define double (curry * 2))

A Tasty dish? Haskell Curry!

A tasty dish? Functions in Haskell • • Currying was named after the Mathematical In Haskell we can define g as a function that takes two arguments of types a and b and returns a logician Haskell Curry (1900-1982) value of type c like this: • Curry worked on combinatory … – g :: (a, b) -> c • A technique that eliminates the need for • We can let f be the curried form of g by variables in … – f = curry g • and hence computer programming! • The function f now has the signature – At least in theory – f :: a -> b -> c • The functional programming language Haskell • f takes an arg of type a & returns a function that is also named in honor of Haskell Curry takes an arg of type b & returns a value of type c

Functions in Haskell Currying in Scheme •All functions in Haskell are curried, i.e., all • Scheme has an explicit built in function, curry, Haskell functions take just single arguments. that takes a function and some of its •This is mostly hidden in notation, and is not arguments and returns a curried function apparent to a new Haskeller • For example: •Let's take the function div :: Int -> Int -> Int which – (define add1 (curry + 1)) performs integer division – (define double (curry * 2)) •The expression div 11 2 evaluates to 5 • We could define this easily as: •But it's a two-part process (define (curry fun . args) –div 11 is evaled & returns a function of type Int -> Int (lambda x (apply fun (append args x)))) –That function is applied to the value 2, yielding 5

1 4/4/2016

Note on lambda syntax Simple example (a) • (lambda X (foo X)) is a way to define a lambda • Compare two lists of numbers pair wise: expression that takes any number of arguments (apply and (map < ‘(0 1 2 3) '(5 6 7 8))) • In this case X is bound to the list of the • Note that (map < ‘(0 1 2 3) '(5 6 7 8)) evaluates argument values, e.g.: to the list (#t #t #t #t) > (define f (lambda x (print x))) • Applying and to this produces the answer, #t > f # > (f 1 2 3 4 5) (1 2 3 4 5) >

Simple example (b) Simple example (c) • Is every number in a list positive? • Is every number in a list positive? (apply and (map < 0 ' (5 6 7 8))) • Use (lambda (x) (< 0 x)) as the function • This is a nice idea, but will not work (apply and (map (lambda (x) (< 0 x)) '(5 6 7 8))) map: expects type as 2nd argument, given: 0; other arguments were: # (5 6 7 8) • This works nicely and gives the right answer

=== context === • What we did was to use a general purpose, /Applications/PLT/collects/scheme/private/misc.ss:74:7 two-argument comparison function (?

Simple example (d) A real world example • Here’s where curry helps • I wanted to adapt a Lisp example by Google’s (curry < 0) ≈ (lambda (x) (< 0 x)) Peter Norvig of a simple program that • So this does what we want generates random sentences from a context (apply and (map (curry < 0) '(5 6 7 8))) free grammar – Currying < with 0 actually produces equivalent of: • It was written to take the grammar and start (lambda x (apply < (append ‘(0) x))) symbol as global variables  – So (curry < 0) takes one or more args, e.g. • I wanted to make this a parameter, but it made ((curry < 0) 10 20 30) => #t the code more complex   ((curry < 0) 10 20 5) => #f • Scheme’s curry helped solve this! [But ‘< taking more than 2 args makes example a toy]

2 4/4/2016

#lang scheme scheme> scheme cfg1.ss ;;; This is a simple … cfg1.ss Welcome to MzScheme v4.2.4 … > (require "cfg1.ss") session (define grammar > (generate 'S) '((S -> (NP VP) (NP VP) (NP VP) (NP VP) (S CONJ S)) (a woman took every mysterious ball) (NP -> (ARTICLE ADJS? NOUN PP?)) > (generate 'S) (VP -> (VERB NP) (VERB NP) (VERB NP) VERB) (a blue man liked the worm over a mysterious woman) (ARTICLE -> the the the a a a one every) > (generate 'S) (NOUN -> man ball woman table penguin student book (the large computer liked the dog in every mysterious student in the dog worm computer robot ) mysterious dog) … > (generate ‘NP) (PP -> (PREP NP)) (a worm under every mysterious blue penguin) (PP? -> () () () () PP) > (generate ‘NP) )) (the book with a large large dog)

Five possible rewrites for a S: If phrase is a list, like (NP VP), #lang scheme (define (generate phrase) 80% of the time it => NP VP and then map generate over it cfg1.ss ;; generate a random sentence or phrase fromand grammar append thecfg1.ss results ;;; This is a simple … 20% of the time it is a conjoined (cond ((list? phrase) If a non-terminal, select sentence, S CONJ S (apply append (map generate phrase))) (define grammar a random rewrite and ((non-terminal? phrase) apply generate to it. '((S -> (NP VP) (NP VP) (NP VP) (NP VP) (S CONJ S)) (generate (random-element (rewrites phrase)))) (NP -> (ARTICLE ADJS? NOUN PP?)) (else (list phrase)))) It’s a terminal, so just (VP -> (VERB NP) (VERB NP) (VERB NP) VERB)Terminal symbols (define (non-terminal? x) return a list with it as (e.g, the, a) are (ARTICLE -> the the the a a a one every) ;; True iff x is a on-terminal in grammar the only element. recognized by (assoc x grammar)) (NOUN -> man ball woman table penguin studentvirtue ofbook not (define (rewrites non-terminal) dog worm computer robot ) heading a ;; Return a list of the possible rewrites for non-terminal in grammar … grammar rule. (rest (rest (assoc non-terminal grammar)))) (PP -> (PREP NP)) () is like ε in a rule, so (PP? -> () () () () PP) 80% of the time a PP? (define (random-element list) ;; returns a random top-level element from list )) produces nothing and 20% a PP. (list-ref list (random (length list))))

> (load "cfg2.ss") cfg2.ss Parameterizing generate > (generate) (a table liked the blue robot) session • Let’s change the package to not use global > (generate grammar 'NP) variables for grammar (the blue dog with a robot) • The generate function will take another > (define g2 '((S -> (a S b) (a S b) (a S b) ()))) parameter for the grammar and also pass it to > (generate g2) (a a a a a a b b b b b b) non-terminal? and rewrites > (generate g2) • While we are at it, we’ll make both param- (a a a a a a a a a a a b b b b b b b b b b b) eters to generate optional with appropriate > (generate g2) defaults () > (generate g2) (a a b b)

3 4/4/2016

(define default-grammar '((S -> (NP VP) (NP VP) (NP VP) (NP VP)) ...)) (define default-grammar '((S -> (NP VP) (NP VP) (NP VP) (NP VP)) ...)) Global variables (define default-start 'S) cfg2.ss (define default-start 'S) cfg2.ss define defaults (define (generate (grammar default-grammar) (phrase default-start)) (define (generate (grammar default-grammar) (phrase default-start)) ;; generate a random sentence or phrase from grammar ;; generate a random sentence or phrase from grammar (cond ((list? phrase) optional (cond ((list? phrase) (apply append (map generate phrase))) parameters (apply append (map generate phrase))) ((non-terminal? phrase grammar) ((non-terminal? phrase grammar) (generate grammar (random-element (rewrites phrase grammar)))) (generate grammar (random-element (rewrites phrase grammar)))) (else (list phrase))))) Pass value of (else (list phrase))))) (define (non-terminal? x grammar) grammar to (define (non-terminal? x grammar) generate takes 2 args – we ;; True iff x is a on-terminal in grammar subroutines ;; True iff x is a on-terminal in grammar want the 1st to be grammar’s (assoc x grammar)) (assoc x grammar)) current value and the 2nd to (define (rewrites non-terminal grammar) Subroutines (define (rewrites non-terminal grammar) come from the list ;; Return a list of the possible rewrites for non-terminal in grammar take new ;; Return a list of the possible rewrites for non-terminal in grammar (rest (rest (assoc non-terminal grammar)))) parameter (rest (rest (assoc non-terminal grammar))))

(define default-grammar '((S -> (NP VP) (NP VP) (NP VP) (NP VP)) ...)) (define default-start 'S) cfg2.ss Curried functions (define (generate (grammar default-grammar) (phrase default-start)) ;; generate a random sentence or phrase from grammar • Curried functions have lots of applictions in (cond ((list? phrase) programming language theory (apply append (map (curry generate grammar) phrase))) ((non-terminal? phrase grammar) • The curry operator is also a neat trick in our (generate grammar (random-element (rewrites phrase grammar)))) functional programming toolbox (else (list phrase)))))

(define (non-terminal? x grammar) • You can add them to Python and other ;; True iff x is a on-terminal in grammar languages, if the underlying language has the (assoc x grammar)) right support (define (rewrites non-terminal grammar) ;; Return a list of the possible rewrites for non-terminal in grammar (rest (rest (assoc non-terminal grammar))))

4