----------------------------------------------------------------------------
I.  Strachey-style denotational semantics: "what is the _meaning_ of ...?"
----------------------------------------------------------------------------

Q: What is the meaning of a numeral?  A: a number

Q: What is the meaning of a storage vector?
A: a _function_ that takes a var as input and returns a number as output!

Q: What is the meaning of an assignment?
A: a _function_ that takes a storage vector as input and returns
   a storage vector as output!

Q: How do we implement these meanings?  A: Study hardware and compilers....


Example:    The meaning of this storage vector:

 X Y Z ...
+-+-+-+   +-+
|0|0|0|...|0|    is    lam i. 0
+-+-+-+   +-+

Say that we apply   Y = 2  to the above vector;
what is the _meaning_ of the output ?

It is this function:      lam j. if (j=Y) then 2 else  ((lam i.0) j)
                       =  lam j. if (j=Y) then 2 else 0

Of course, we can code  j=Y  and the  if-then-else  in  lambda-calculus, too!
Or, we can do what Strachey did:  augment the lambda-calculus with new
constants and rewrite rules (delta-rules), like this:


(a) Bool-type constants and rules:

constants:  true, false
operators: if
rules: 
       (if true E1 E2) => E1       (if false E1 E2) => E2

(b) Int-type constants and rules:

constants:  0,1,2,...
operators:  plus,  minus,  times,  ...,  equals
rules:  
        (plus 0 0) => 0     (plus 1 0) => 1   ...
        (minus 0 0) => 0    (minus 1 0) => 1   ...
        (times 0 0) => 0    (times 1 1) => 1    ...
        (equals 0 0) => true    (equals 1 0) => false

(c) Var-type constants and rules:

constants:  X, Y, Z, A, B, ...
operators: =
rules:
       (X = X) => true    (X = Y) => false   ...

(d) Store-type constants and rules:

constants: none (use  lam-abstractions)
operators:  lookup,  update
rules:
       (lookup i s) =>  (s i)
       (update i n s) =>   lam j. if (j = i)  n  (lookup j s)


### A quick example:

let  [[ Y = 2 ]] == lam s. (update Y 2 s)
then
     [[ Y = 2 ]](lam i. 0) =>  (lam s.  update Y 2 s)(lam i. 0)
                           =>  update Y 2 (lam i. 0)
                           =>  lam j. if (j = i)  2  (lookup j (lam i. 0))
                           =>  lam j. if (j = i)  2  ((lam i. 0) j)
                           =>  lam j. if (j = i)  2  0

That is, the _meaning_ of  Y = 2  applied to the _meaning_ of a store of  0s
is a store whose _meaning_ is   
                                  lam j. if (j = i)  2  0

How do I implement this?    Study hardware and compilers....



----------------------------------------------------------------------------
II.  A denotational semantics shows how to define a program's _meaning_ 
----------------------------------------------------------------------------

C: Command           E: Expression
I: Variable          N: Numeral

C ::=  I = E  |  C1 ; C2  |  if E then C1 else C2  |   while E do C

E ::=  N  |  I  |  E1 + E2  |  E1 - E2


[[ C ]] : Store -> Store

  [[ I = E ]] = lam s. update I  ([[ E ]] s)  s

  [[ C1 ; C2 ]] =  lam s.  [[ C2 ]] ([[ C1 ]] s)

  [[ if E then C1 else C2 ]]
        =  lam s. if (equals 0 ([[ E ]] s))  ([[ C2 ]] s)  ([[ C1 ]] s)

  [[ while E do C ]] 
      =  lam s. Y (lam w.
                    lam s'. if (equals 0 ([[ E ]] s')) s' (w ([[ C ]] s'))
                  )

      where  Y = lam f. (f (x x))(f (x x)),  as usual


[[ E ]] : Store -> Int

  [[ N ]] = lam s. n     that is, the int, n, named by numeral, N

  [[ I ]] = lam s. lookup I s

  [[ E1 + E2 ]] = lam s. plus ([[ E1 ]] s)  ([[ E2 ]] s)

  [[ E1 - E2 ]] = lam s. minus ([[ E1 ]] s)  ([[ E2 ]] s)


How do I implement this?    Study hardware and compilers....




----------------------------------------------------------------------------
III.  Example:  What is the meaning of  

                X = 1; Z = X * Y   

          when used with the meaning of a zero-ed store ?
----------------------------------------------------------------------------


[[ X = 1; Z = X * Y ]] (lam i. 0)

=  [[ Z = X * Y ]] ([[ X = 1 ]] (lam i. 0))

=  [[ Z = X * Y ]] ([[ X = 1 ]]                     (lam i. 0))

=  [[ Z = X * Y ]] ((lam s. update X ([[ 1 ]] s) s) (lam i. 0))

=  [[ Z = X * Y ]] (update X ([[ 1 ]]    (lam i. 0)) (lam i. 0))

=  [[ Z = X * Y ]] (update X ((lam s. 1) (lam i. 0)) (lam i. 0))

=  [[ Z = X * Y ]] (update X 1                       (lam i. 0))

=  [[ Z = X * Y ]] (lam j. if (j=X) 1 ((lam i. 0) j))

=  [[ Z = X * Y ]] (lam j. if (j=X) 1 0)            )

=  (lam s. update Z ([[ X * Y ]] s) s)                 (lam j. if (j=X) 1 0) 

=  (lam s. update Z (times ([[ X ]] s) ([[ Y ]] s) s)) (lam j. if (j=X) 1 0) 


=  (lam s. update Z (times ([[ X ]] s)
                           ([[ Y ]] s) s)
   )
           (lam j. if (j=X) 1 0) 

=  (lam s. update Z (times ((lam s. lookup X s) s)
                           ([[ Y ]] s) s)
   )
           (lam j. if (j=X) 1 0) 

=  (lam s. update Z (times (lookup X s)
                           ([[ Y ]] s) s)
   )
           (lam j. if (j=X) 1 0) 


=  (lam s. update Z (times (s X) 
                           ([[ Y ]] s) s)
   )
           (lam j. if (j=X) 1 0)


=  (lam s. update Z (times (s X)       
                           (s Y)  s)
   )
           (lam j. if (j=X) 1 0)


=  update Z (times ( (lam j. if (j=X) 1 0) X)
                   ( (lam j. if (j=X) 1 0) Y))
            (lam j. if (j=X) 1 0)

=  update Z (times  1
                   ( (lam j. if (j=X) 1 0) Y))
            (lam j. if (j=X) 1 0)

=  update Z (times  1
                    0)
            (lam j. if (j=X) 1 0)

=  update Z 0 (lam j. if (j=X) 1 0)

=  lam j'. if (j'=Z) 0 (((lam j. if (j=X) 1 0) j'))

=  lam j'. if (j'=Z) 0 (if (j'=X) 1 0) 


The meaning of the program applied to the meaning of a zero-out store
is a (store) function that maps Z to 0,  X to 1, and all other vars to 0.



###Exercise: calculate the meaning of this program, without any input:

[[ X = 1;  Z = X * Y ]]


If you proceed correctly, you will reach this meaning:

[[ X = 1;  Z = X * Y ]]  =

lam s. (lam s1. update Z (times (lookup X s1) (lookup Y s1) s1) (update X 1 s)


This is the _partially evaluated_ denotation of the original program.
It is a kind of meta-"compiled code".   There is a rich theory of
compiling based on dentational semantics.