Please send questions to st10@humboldt.edu .

; building on the idea of higher-order functions

; higher-order functions:
; either have one or more parameters that are functions
;    AND/OR, returns a function;

; reminder:
; my-map: expects a function and a list,
;         and returns the list resulting from applying that
;         function to every element in a list

(define (my-map a-funct a-list)
    (cond
        ((null? a-list) '())
        (else (cons (a-funct (car a-list))
                    (my-map a-funct (cdr a-list))))
    )
)

(my-map car '((cheese 3.50) (tofu 2.98) (chocolate 4.50)))

; and in higher order functions, it is convenient (or more)
;    to have the concept of an anonymous function;

; and McCarthy adapted Church's notation from lambda calculus
;    for specifying anonymous functions

(lambda (x) 
  (+ 3 x)
)

(lambda (length width height)
  (* length width height)
)

; the value of each of the these lambda expressions
;    is an anonymous function -- of type procedure (!)
;    in R5RS Scheme, anyway...

; you apply a named function such as car by simply typing
;    its name after an open parenthesis, followed by its
;    arguments;
; car is a simple expression of type procedure

; you apply an anonymous function, then, by putting
;    a lambda expression after an open parenthesis,
;    followed by the arguments for that anonymous function;

((lambda (length width height)
   (* length width height)) 3 5 10)

; this is how functions were originally defined in Lisp:

(define box-area
   (lambda (length width height)
     (* length width height)))

(box-area 3 5 10)

((lambda (a-num) (< a-num 0)) 
 13)

(my-map (lambda (person)
            (list "howdy" person))
        '("harold" "maude" "alice"))

; and it is quite reasonable, then, to return a function
;    by having the function return a lambda expression

; here is a rather silly function that expects 2 arguments
;    (of pretty much any type) and produces a function that
;     can be used to "combine" those two things...

; 2 numbers? returns a function that adds them
; 2 lists? returns a function that appends them
; 2 non-list non-numbers? returns a function that puts them into a list
; 1 non-list and 1 list? returns a function that cons's them
; 1 list and 1 non-list? appended the list to a list consisting of
;    the non-list

(and #t #t)
(or #t #f)
(not #f)

(define (get-combiner thing1 thing2)
  (cond
      ((and (number? thing1) (number? thing2))
         (lambda (n1 n2) 
           (+ n1 n2)))
      
      ((and (not (list? thing1)) (not (list? thing2)))
          (lambda (item1 item2)
            (list item1 item2)))
      
      ((and (not (list? thing1)) (list? thing2))
           (lambda (item a-list)
             (cons item a-list)))
      
      ((and (list? thing1) (not (list? thing2)))
           (lambda (a-list item)
             (append a-list (list item))))
      
      ((and (list? thing1) (list? thing2))
           (lambda (list1 list2)
             (append list1 list2)))
  )
)

; expects 2 things, and calls get-combiner to
;    get an appropriate function to combine them,
;    which it then uses on those things

(define (add-to thing1 thing2)
  ((get-combiner thing1 thing2) thing1 thing2))
          
(add-to 1 3)
(add-to 'a 'b)
(add-to '(hi there) 3)
(add-to 3 '(hi there))
(add-to 'hi 3)
(add-to '(1 2) '(3 4))

; a more serious reason for returning a function:
;    currying

; (Wikipedia)
; (CS) "currying is the technique of transforming a function taking
;    multiple arguments into a function that takes a single argument
;    (the first of the arguments to the original function)
;    and returns a new function which takes the remainder of the
;    arguments and returns the result"

; (named by Christopher Strachey after logician Haskell Curry
; (and that's the namesake for the Haskell functions=al language...)
; although it was invented by Schonfinkel and Frege...!)

(define (plus x y)
  (+ x y)
)

; the curried version of plus would take a single argument x and
;    return a new function which takes a single argument y and
;    returns x + y

(define (curried-plus x)
  (lambda (y) 
    (+ x y))
)

(curried-plus 1) ; produces a function that adds 1 to its argument
((curried-plus 1) 28)

(curried-plus 100) ; produces a function that adds 100 to its argument
((curried-plus 100) 27)
        
; in theoretical CS, currying gives you a way to study functions
;    with multiple arguments in very simple theoretical models
;    (like the lambda calculus) where functions can only take
;    a single argument...

; in a practical sense, sometimes the resulting functions are
;    useful (++, anyone?)

; Haskell, ML provide syntactic sugar for currying --
;    but any language w/ support for higher-order functions
;    should be able to used to write curried functions...

; let/let*/letrec are ways to locally give a name a value
;    in Scheme/LISP for convenience -- more on this later;

; interactive input:
; read function: expects no arguments
;    when evaluated, it stops until the user types something
;       ended with a return/enter
;    and its value is what the user types

(read)
(< (read) (read))

; yes, there's file i/o too...