MORE DETAILS ON LAMBDA

Formally, lambda has the following syntax and semantics:

(lambda (arg1 ... argn)
  body)

where arg1 ... argn are names and body is any Scheme expression.

When DrScheme sees a lambda expression, it evaluates it as follows:

(local ((define (a-new-name arg1 ... argn)
	  body))
  a-new-name)

Note that the body expression cannot refer to a-new-name because
DrScheme, not the programmer, introduces a-new-name.

A NOTE ON PROGRAMS AS VALUES

Since programs are values, you can put a program anywhere that Scheme
allows a value.  For example:

(list (lambda (num) (< num 5))
      (lambda (num) (< num 6))
      (lambda (num) (< num 7)))

is a list containing three programs.

TYING IT ALL TOGETHER 

Let's look at a larger example that ties together what we've been
discussing the last few classes.

Consider a simple board game.  Each character in the game has a name,
a number of lives, a sequence of moves, and a spell to cast on the
characters.  Characters move around the board and cast spells
according to some (irrelevant) rules.  At certain points during the
game, players may draw cards that change their characters' spells.  
The game supports four types of spells:

  - reverse the left and right turns in the moves of all characters
  - remove (ie, kill off) all characters with at least 6 lives
  - wake up all dead characters by giving them one life
  - wake up all dead characters by giving them three lives

From this description, spells sound like functions that consume and
produce lists of characters.  We want to write a program to handle the
process of updating the spells stored in each character.  Here are the
data definitions that are relevant to this task.  

;; A move is one of
;;  - a number
;;  - 'left
;;  - 'right

;; A char is a structure
;;   (make-char N L M S)
;; where N is a symbol,
;;       L is a number,
;;       M is a (listof move), and
;;       S is function ((listof char) -> (listof char))

(define-struct char (name lives moves spell))

;; A spell-type is one of
;;  - 'rev-turns, 
;;  - 'rem-healthy,
;;  - 'wake1, or
;;  - 'wake3,

First, let's look at an example character with a spell that doesn't do 
anything:

(make-char 'Cat 9 (list 2 'left 1 'right) (lambda (aloc) aloc))

Our desired program, update-spell, has the following contract and purpose:

;; update-spell : char spell-type -> char
;; creates a character whose spell has been updated according to the type 

Let's start by writing the body of this program minus the actual new
spells: 

(define (update-spell a-char type)
  (local ((define (change-spell a-spell)
            (make-char (char-name a-char)
                       (char-lives a-char)
                       (char-moves a-char)
                       a-spell)))
    (cond [(symbol=? type 'rev-turns) (change-spell ...)]
          [(symbol=? type 'rem-healthy) (change-spell ...)]
          [(symbol=? type 'wake1) (change-spell ...)]
          [(symbol=? type 'wake3) (change-spell ...)])))

Now, let's fill in each ... in order.

1. What do we need to do to reverse a character's turns?  We know how
to write such a program:

;; rev-char-turns : char -> char
;; creates a character with the turns reversed
(define (rev-char-turns a-char)
  (local ((define (rev-one-move a-move)
            (cond [(number? a-move) a-move]
                  [(symbol=? a-move 'left) 'right]
                  [(symbol=? a-move 'right) 'left]))
          (define (rev-turns lom)
            (map rev-one-move lom)))
    (make-char (char-name a-char)
               (char-lives a-char)
               (rev-turns (char-moves a-char))
               (char-spell a-char))))
          
How do we write a spell to reverse the turns of all characters?  We
said that a spell is function from (listof char) to (listof char), so
our new spell must consume a (listof char)

	(lambda (aloc) ...)

How do we get the updated list of characters from this spell?

	(lambda (aloc) 
          (map rev-char-turns aloc))

This fills in the ... for the 'rev-turns case of update spell.

2. Next, we need to remove all the characters with at least 6 lives.
As in the previous case, the spell must consume and produce a (listof
char): 

	(lambda (aloc) ...)

Keeping only characters that satisfy a certain criterion is precisely
the purpose of filter.  Therefore, we can use filter to write our new
spell:

	(lambda (aloc)
	  (filter ... aloc)).

We must replace ... with the function that decides which characters to 
keep.  We want to keep any character with less than 6 lives, so we
could write the following:

	(lambda (aloc)
	  (filter (lambda (a-c)
		    (< (char-lives a-c) 6))
		  aloc))

This fills in the ... for the 'rem-healthy case.

3. Finally, we need to fill in the two cases for waking up dead
characters.  The only difference between these two spells is that one
wakes up characters with one life and the other with three.
Therefore, we should be able to write one function that handles both
tasks: 

(define (wake-dead num-lives aloc)
  (map (lambda (a-char)
	 (cond [(< (char-lives a-char) num-lives)
		(make-char (char-name a-char)
			   num-lives
			   (char-moves a-char)
			   (char-spell a-char))]
	       [else a-char]))
       aloc))

Can we use wake-dead as the spell in the 'wake1 and 'wake3 cases?  No,
because it doesn't meet the contract.  The contract requires a spell
to be function taking only a list of characters.

This is a case where we want to supply the arguments to a given
function at different times.  We can supply the number of lives at the 
time that we create the spell, but we cannot supply the list of
characters until we actually cast the spell.  How do we delay
specifying some of the arguments?  We can write another program that
consumes the number of lives now and produces a function that will
consume the list of characters when it becomes available:

(define (make-wake-dead num-lives)
  (lambda (aloc)
    (map (lambda (a-char)
	   (cond [(< (char-lives a-char) num-lives)
		  (make-char (char-name a-char)
			     num-lives
			     (char-moves a-char)
			     (char-spell a-char))]
		 [else a-char]))
	 aloc)))

This process of creating a layer of functions, each taking a subset of 
the arguments, is called currying.  (Historical note: the name comes
from ??? Curry, who is credited with the approach.  The approach is
actually due to Shoenfinkle.  Were history correct, we'd call this
process Shoenfinkling.)

With make-wake-dead, we can finish the cases for 'wake1 and 'wake3 as
follows: 

	(make-wake-dead 1)
	(make-wake-dead 3)

Putting it all together, the final program appears as follows:

;; A move is one of
;;  - a number
;;  - 'left
;;  - 'right

;; A char is a structure
;;   (make-char N L M S)
;; where N is a symbol,
;;       L is a number,
;;       M is a (listof move), and
;;       S is function ((listof char) -> (listof char))

(define-struct char (name lives moves spell))

;; A spell-type is one of
;;  - 'rev-turns, 
;;  - 'rem-healthy,
;;  - 'wake1, or
;;  - 'wake3,

;; update-spell : char spell-type -> char
;; creates a character whose spell has been updated according to the type 
(define (update-spell a-char type)
  (local ((define (change-spell a-spell)
            (make-char (char-name a-char)
                       (char-lives a-char)
                       (char-moves a-char)
                       a-spell)))
    (cond [(symbol=? type 'rev-turns) 
           (change-spell (lambda (aloc)
                           (map rev-char-moves aloc)))]
          [(symbol=? type 'rem-healthy) 
           (change-spell (lambda (aloc)
                           (filter (lambda (a-c)
                                     (< (char-lives a-c) 6))
                                   aloc)))]
          [(symbol=? type 'wake1) (change-spell (make-wake-dead 1))]
          [(symbol=? type 'wake3) (change-spell (make-wake-dead 3))])))
          
;; rev-char-turns : char -> char
;; creates a character with the turns reversed
(define (rev-char-turns a-char)
  (local ((define (rev-one-move a-move)
            (cond [(number? a-move) a-move]
                  [(symbol=? a-move 'left) 'right]
                  [(symbol=? a-move 'right) 'left]))
          (define (rev-turns lom)
            (map rev-one-move lom)))
    (make-char (char-name a-char)
               (char-lives a-char)
               (rev-turns (char-moves a-char))
               (char-spell a-char))))
          
;; make-wake-dead : num -> ((listof char) -> (listof char))
;; creates a function that consumes a list of chars and
;;   gives each dead character the indicated number of lives
(define (make-wake-dead num-lives)
  (lambda (aloc)
    (map (lambda (a-c)
           (local ((define updated-char
                     (make-char (char-name a-c)
                                num-lives
                                (char-moves a-c)
                                (char-spell a-c))))
             (cond [(< (char-lives a-c) 1) updated-char]
                   [else a-c])))
         aloc)))