Programming Languages: Chapter 8: Currying and Higher-order Functions

Variable-length argument lists in Scheme

Arguments to any function are always passed in as list. It is up to the programmer to decompose that argument list and group individual arguments in the formal parameter specification of the function definition using dot notation, if necessary.
```(define f (lambda (x) x))

(f 1)
(f '(1 2 3))

;;; x is just the list (1 2 3)
(define f (lambda x x))

(f 1 2 3)
(f 1)

;;; uses pattern matching like ML and Haskell
;;; g and h take a variable number of arguments
(define g (lambda (x . y) x))
(define h (lambda (x . xs) xs))

;;; only 1 argument passed
(g '(1 2 3))
(h '(1 2 3))
(write "a")
(newline)

;;; now 2 arguments passed
(g 1 '(2 3))
(h 1 '(2 3))

;;; now 3 arguments passed
(g 1 2 3)
(h 1 2 3)
```

Kleene's Smn theorem

• also known as the translation lemma, parameter theorem, or parameterization theorem from computability theory
• formally, states that for any function f (x1, x2, ..., xn),

f (a1, a2, ..., am) = g (xm+1, xm+2, ..., xn), where m < n, such that g (am+1, am+2, ..., an) = f (a1, a2, ..., am, am+1, am+2, ..., an).

```pow (0, _) = 1
pow (1, b) = b
pow (_, 0) = 0
pow (e, b) = b * pow (e-1, b)

powc 0 _ = 1
powc 1 b = b
powc _ 0 = 0
powc e b = b * powc (e-1) b

-- notice anything interesting?

Main> :type pow
pow :: (Num a, Num b) => (b,a) -> a

Main> :type powc
powc :: (Num a, Num b) => b -> a -> a

square = powc 2
cube = powc 3

-- notice anything interesting?

Prelude> :type map
map :: (a -> b) -> [a] -> [b]
Prelude> :type foldl
foldl :: (a -> b -> a) -> a -> [b] -> a
Prelude> :type foldr
foldr :: (a -> b -> b) -> b -> [a] -> b
Prelude>
```
• parenthesizing an operator converts it from an infix to prefix operator:
```-- called a 'section'
inc = (+) 1
inc1 = (+) 1.0

Main> :type inc
inc :: Integer -> Integer
Main> :type inc1
inc1 :: Double -> Double
Main>

-- [PIH] p. 118
-- currying obviates the need for
-- the helper function insertineach
powerset [] = [[]]
powerset (x:xs) =
let
temp = powerset xs
in
(map (x:) temp) ++ temp

Main> :type powerset
powerset :: [t] -> [[t]]
```
• functions curry and uncurry
```Prelude> :type curry
curry :: ((a, b) -> c) -> a -> b -> c

Prelude> :type uncurry
uncurry :: (a -> b -> c) -> (a, b) -> c
```

Curried form in ML

```fun pow 0 _ = 1
|   pow 1 b = b
|   pow _ 0 = 0
|   pow e b = b * pow (e-1) b;

(*
int * int -> int
=>
int -> int -> int
*)

val square = pow 2;
val cube = pow 3;
```
• map, foldl, are foldr are defined in curried form:
```val ourimplode = foldr (op ^) "" o (map str);
(* notice something peculiar about foldr and map *)
```
• see [EMLP] Chapter 5 and, specifically § 5.5 (pp. 168-173), for more information.

Curried Form in Scheme

• any language with first-class closures can be used to define functions in curried form
• first attempt:
```;;; here the curried form is too tightly woven into the function definition
;;; moreover, pow now cannot be completely evaluated
(define pow
(lambda (e)
(cond
((eqv? e 0) 1)
(else
;; return a closure
(lambda (b)
(cond
((eqv? b 0) 0)
((eqv? b 1) 1)
((eqv? e 1) b)
(else (* b ((pow (- e 1)) b)))))))))

> (define square (pow 2))
> (square 4)
16
```
• second attempt:
```(define s11
(lambda (f x)
(list 'lambda '(y) (list f x 'y))))

(define pow
(lambda (e b)
(cond
((eqv? b 0) 0)
((eqv? b 1) 1)
((eqv? e 0) 1)
((eqv? e 1) b)
(else (* b (pow (- e 1) b))))))

> (define square (s11 pow 2))
> square
(lambda (y) (#<procedure:pow> 2 y))
> (eval square)
#<procedure>
> ((eval square) 4)
16
```
• a similar approach:
```;;; notice we must explicitly call papplyn which
;;; means we must know a-priori how many arguments are contained
;;; in the complete argument list of the function we desire to partially apply

;;; however, this approach obviates the need explicitly to call eval and
;;; the need define multiple versions of s (e.g., to partially apply
;;; a 3 argument function in all ways possible would require
;;; s111, s12, and s21).
(define papplyn
(lambda (fun . args)
(lambda x
(apply fun (append args x)))))

(define square (papplyn pow 2))

> (square 4)
16

;; examples from Jeffrey A. Meunier's article ``Function Currying in Scheme''

;; and we can continue to partially apply the result as we desire
(define list1 (papplyn list 'A))
(define list2 (papplyn list1 'tree))
(define list3 (papplyn list2 'grows))

(list3 'in 'Brooklyn.)

;; since the original function (which is list)
;; can accept multiple arguments,
;; the partially applied function can also accept multiple arguments

(define list4 (papplyn list 'A 'tree 'grows))
(list4 'in 'Brooklyn.)
```

Partial evaluation

Generalizes the idea of currying from any prefix of the argument list to any subset of the argument list.

Formally, states that for any function f(x1, x2, ..., xn),

f(a, b, ..., r) = g({x1, x2, ..., xn} - {a, b, ..., r}), where {a, b, ..., r} ⊆ {x1, x2, ..., xn}, such that g(s, t, ..., z) = f(a, b, ..., z).

Mapping

```ourmap f [] = []
ourmap f (x:xs) = (f x):(ourmap f xs)

square n = n*n

ans = ourmap square [1,2,3,4,5,6]

squarelist lon = map square lon

ans2 = squarelist [1,2,3,4,5,6]

vs.

squarelist = map square
```

Functional composition

```add3 x = x+3
mult2 x = x*2

```

Fooooolding lists

use foldr and foldl

best to think of foldl and foldr non-recursively [PIH] pp. 65 & 68  ```-- foldl and foldr take a prefix binary function,
-- a base value of recursion, and a list, in that order

-- foldl :: (a -> b -> a) -> a -> [b] -> a
-- foldl associates from the left:
-- +(+(+(+(0,1),2),3),4) = 10
-- think of foldl as using the accumulator approach
ans5 = foldl (+) 0 [1,2,3,4]

-- -(-(-(-(0,1),2),3),4) = (((0-1)-2)-3)-4 = -10
ans6 = foldl (-) 0 [1,2,3,4]

-- foldr :: (a -> b -> b) -> b -> [a] -> b
-- foldr associates from the right:
-- (1 :: (2 :: (3 :: [])))
-- (1 - (2 - (3 - (4 - 0)))) = -2
ans7 = foldr (-) 0 [1,2,3,4]

-- for reasons of efficiency, use foldl
-- rather than foldr when they produce the
-- same result

sumlist = foldl (+) 0

-- ref. [PIH] pp. 65-66
length1 = foldr (\_ n -> n+1) 0

-- cons reversed
snoc x xs = xs ++ [x]

reverse6a [] = []
reverse6a (x:xs) = snoc x (reverse6a xs)

reverse6 = foldr snoc []

-- ref. [PIH] p. 148
reverse7 = foldl (\xs x -> x : xs) []
```

Putting it all together: higher-order functions

Use these concepts (higher-order functions) to define a function string2int which converts a string representation of an integer to the corresponding integer:
```Main> :type string2int
string2int :: [Char] -> Int
Main> string2int "0"
0
Main> string2int "123"
123
Main> string2int "321"
321
Main> string2int "5643452"
5643452
```
Hint: only requires 2 lines of code, or only 1 if you use a literal function (see below).
```import Data.Char (ord)

char2int sumsofar initChar  = (ord initChar) - (ord '0') + 10*sumsofar

string2int l = foldl char2int 0 l

string2int' = foldl (\r c -> (ord c) - (ord '0') + 10*r) 0

char2int' initChar sumsofar  = (ord initChar) - (ord '0') + 10*sumsofar

-- not recommended, why? hint: two reasons
string2int'' s = foldr char2int' 0 (reverse s)
```

contrast square function in Haskell with that in ML
```square n = n*n

Main> :type square
square :: Num a => a -> a
-- the type of square is a 'qualified type'
-- and Num is a 'type class'
```

Summary

(ref. [WFPM])

Higher-order functions (allows functions to be glued together) and lazy evaluation (allows whole programs to be glued together) are the glue which allows us to combine program components together in creative ways to produce concise, malleable, and reusable programs and, thereby enables modular programming which makes programs easier to debug, maintain, and re-use.

Mapping

```fun ourmap f nil = nil
| ourmap f (x::xs) = (f x)::ourmap f xs;

fun square x = x*x;

ourmap square [1,2,3,4,5,6];

fun squarelist lon = map square lon;

squarelist [1,2,3,4,5,6];

vs.

val squarelist = map square;
```

Functional composition

ML's composition operator o
```fun add3 x = x+3;
fun mult2 x = x*2;

(* val it = 12 : int *)

(* val it = 9 : int *)

(* op converts an infix operator to a prefix operator *)
(* be careful *)
(op *) (4, 5); (* doesn't work *)
(op * ) (4, 5); (* now it works *)

```

Fooooolding lists

use foldr and foldl  ```(* foldl and foldr take a prefix binary function,
a base value of recursion, and a list, in that order *)

(* the standard ML foldl seems to have the wrong type!
foldl : ('a * 'b -> 'b) -> 'b -> 'a list -> 'b

foldl associates from the left (e.g.,
+(+(+(+(0,1),2),3),4) = 10)
think of foldl as using the accumulator approach *)
foldl (op +) 0 [1,2,3,4];

(* -(4, -(3, -(2, -(1,0)))) = 4-(3-(2-(1-0))) = 2 *)
foldl (op -) 0 [1,2,3,4];

(* foldr : ('a * 'b -> 'b) -> 'b -> 'a list -> 'b
foldr associates from the right (e.g.,
(1 :: (2 :: (3 :: []))))
(1 - (2 - (3 - (4 - 0)))) = ~2 *)
foldr (op -) 0 [1,2,3,4];

val sumlist = foldr (op +) 0;
```

Putting it all together: higher-order functions

• now we use these concepts to define our own implode function
```implode (explode ("apple"));
(* val it = "apple" : string *)
val exploded = explode ("apple");
(* val exploded = [#"a",#"p",#"p",#"l",#"e"] : char list *)
val apple = implode ([#"a",#"p",#"p",#"l",#"e"]);
(* val apple = "apple" : string *)

(* first attempt at implementing our own version of implode *)
val combine x = foldr (op ^) #"";
```
why will this not work?
```(* str converts from char to string *)
(* char list -> string *)
val ourimplode = (foldr op ^ "") o (map str); (* this works *)
```
• converting a string representing an integer to an integer
```(* example: 123 = (3+0) + (2*10) + (1*100) *)
fun char2int (c, v) = ord (c) - ord (#"0") + 10*v;
(* val char2int = fn : char * int -> int *)

(* char2int (#"3", char2int (#"2", char2int (#"1", 0))); *)
foldl char2int 0 (explode ("123"));
(* val it = 123 : int *)

fun string2int s = foldl char2int 0 (explode s);

fun string2int1 s = foldl (fn (c, v) => ord (c) - ord (#"0") + 10*v) 0 (explode s);
```

References

 [EMLP] J.D. Ullman. Elements of ML Programming. Prentice Hall, Upper Saddle River, NJ, Second edition, 1997. [PIH] G. Hutton. Programming in Haskell. Cambridge University Press, Cambridge, 2007. 