# Notes on Exam 2: Semantics, Functional Programming, and More

Some time has progressed since I wrote the draft of the exam solutions. I am no longer sure whether there are any problems. Please let me know if you observe any.

## Problems

### 1. Input Expressions in SIMPLE

In some languages, it's possible to use the input statement in expressions, so that it reads and returns a value, rather than reading a value and storing it in a variable. For example, instead of

you would write

You can also use read as part of an expression, as in

x = 2 * read() - 5;

Consider the accompanying description of the semantics of SIMPLE. What changes would we need to make to the SIMPLE semantics to accommodate this new, no-argument, read() function? Note that we would also delete the old read(I).

After summarizing the changes, write the complete new semantics.

Most of the changes are similar to those we considered in adding an ``assignment expression'' in assignment 3.

• We need to update the abstract syntax to remove the read statement and add a read expression.
• We need to update the type of meaningExp so that it takes input as a parameter, and ``returns'' the modified input (continuations seem to be the best way to do this).
• We probably need an expression continuation domain to simplify this.
• We need to change all the existing definitions of meaningExp to accomodate the new type.
• We need to change all the uses of meaningExp to accomodate the new type.

So let's get on with it.

#### Syntactic Domains

There are no changes to the syntactic domains.

P in Prog
L in SL
S in Stat
E in Exp
V in Val
I in Ide

#### Abstract Syntax

We need to drop

We get the following.

// Programs
Prog => L
// Lists of statements
SL => S
|  S ; L
// Statements
Stat => I = E
|  if E then L fi
|  if E then L0 else L1 fi
|  while E do L od
|  write(E)
// Expressions
Exp => E1 + E2
|  E1 * E2
|  E1 - E2
|  E1 / E2
|  (E)
|  V
|  I

#### Semantic Domains

We need to add K for expression continuations. Note that expression continuations are a lot like normal continuations, except that they take a value as their first parameter. I've used a shorthand (albeit a completely correct one) to show this.

n in N                   Natural Numbers
env in U = Id -> N       Environments
inp in I = N*            Input (a sequence of numbers)
out in O = N*            Output (a sequence of numbers)
cont in C = U -> I -> O  Continuations
econt in K = N -> C      Expression continuations

#### Types of the Semantic Functions

We need to change meaningExp so that it also takes I and K as parameters. It now simply returns output, rather than the result value (the continuation takes care of the rest).

meaningProg : Prog -> I -> O
meaningSL : SL -> C-> U -> I -> -> O
meaningStat : Stat -> C -> U -> I -> O
meaningExp : Exp -> U -> I -> K -> O
meaningVal : Val -> N

#### Definitions of the Semantic Functions

##### meaningProg

No change, since it doesn't use meaningExp.

meaningProg[[SL]] = meaningSL[[SL]] cleanup basicEnv
##### meaningSL

No change, since it doesn't use meaningExp.

meaningSL[[S;L]] =
\cont . \env . \inp .
meaningS[[S]] (meaningSL[[L]]] cont) env inp
meaningSL[[S]] = meaningS[[S]]
##### meaningStat

We need to delete meaningStat[[read(I)]], since there is no longer a read(I) statement.

We need to change meaningStat[[write(E)]], since it uses meaningExp.

We need to change meaningStat[[I = E]], since it uses meaningExp.

We need to change meaningStat[[while E do L od]], since it uses meaningExp.

meaningStat[[write(E)]] =
\cont . \env . \inp .
meaningExp E env (\n . \env' . \inp' .
cons n (cont env' inp'))
meaningStat[[I = E]] =
\cont . \env . \inp .
meaningExp E env (\n . \env' . \inp' .
cont (setEnv env' I n) inp'
meaningStat [[if E then L1 else L2 fi]] =
\cont . \env . \inp .
(meaningExp E env) = 0 -> meaningSL L2 cont env inp
, meaningSL L1 cont env inp
meaningStat [[if E then L fi]] =
\cont . \env . \inp .
(meaningExp E env) = 0 -> cont env inp
; meaningSL L cont env inp
meaningStat[[while E do L od]] = F(E,L) s.t.
F(E,L) : C -> U -> I -> O
F(E,L) cont env inp =
meaningExp E env inp (\n . \env' . \inp' .
(n == 0) -> cont env' inp'
, meaningSL L (\e . \i . F(E,L) cont e i)
env' inp'
##### meaningExp

We need to change every one of these equations to accomodate the new type. We need to change many to accomodate the recursive calls to meaningExp. We need to add one for read.

The binary operations are relatively straightforward. I've made the decision to evaluate left-to-right, since it's difficult (and not worth my time or yours) to evaluate them in ``arbitrary'' order.

meaningExp [[E1 + E2]] =
\env . \inp . \econt .
meaningExp E1 env inp (\n1 . \env1 . \inp1 .
meaningExp E2 env1 inp1 (\n1 . \env1 . \inp1 .
econt (n1 + n2) env2 inp2))
meaningExp [[E1 - E2]] =
\env . \inp . \econt .
meaningExp E1 env inp (\n1 . \env1 . \inp1 .
meaningExp E2 env1 inp1 (\n1 . \env1 . \inp1 .
econt (n1 - n2) env2 inp2))
meaningExp [[E1 * E2]] =
\env . \inp . \econt .
meaningExp E1 env inp (\n1 . \env1 . \inp1 .
meaningExp E2 env1 inp1 (\n1 . \env1 . \inp1 .
econt (n1 * n2) env2 inp2))
meaningExp [[E1 / E2]] =
\env . \inp . \econt .
meaningExp E1 env inp (\n1 . \env1 . \inp1 .
meaningExp E2 env1 inp1 (\n1 . \env1 . \inp1 .
econt (n1 / n2) env2 inp2))

The simple recursive call just ``removes the parentheses'', and doesn't require any other changes. (That's one of the advantages of not listing all the parameters :-)

meaningExp [[(E)]] =
meaningExp E

The remaining basic expressions are fairly easy, since they don't involve recursion.

meaningExp [[I]] =
\env . \inp . \econt .
econt (env I) env inp
meaningExp [[V]] =
\env . \inp . \econt .
econt (meaningVal V) env inp

That leaves us with just the new one. We strip off the front of the input list, and use it as the number parameter.

\env . \inp . \econt .
econt (car inp) env (cdr inp)

Doesn't change.

#### Helper Functions

Don't change.

// Build a basic environment that assigns 0 to every variable.
basicEnv : U
basicEnv = \x . 0
// A continuation used to ``clean up'' at the end of the program.
cleanup : C
cleanup = \env . \inp . nil
// Set the value associated with an identifier in an environment.
setenv : U -> Id -> N -> U
setenv env id num =
\x . (x == id) -> num , (env id)

### 2. The Semantics of Scheme

Explain the semantics of each of the following standard functions, given on pages 42 and 43 of the Scheme report.

a. cons

b. setcar

c. eqv

d. apply

### 3. Fibonacci Numbers

The Fibonacci sequence -- 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, ... -- is defined recursively as

• fib(0) = 0
• fib(1) = 1
• fib(n) = fib(n-1) + fib(n-2), for all n > 1

Unfortunately, the natural translation of this into program code leads to an algorithm with exponential running time. Fortunately, it is also possible to write an O(n) implementation iteratively, by keeping track of the previous two values and adding them at each iteration.

Write a O(n) Fibonacci function in FP. You may write this function iteratively or recursively. Make sure that your code is correct, working, and tested.

There are clearly a number of ways to do this problem. I'm going to use a helper function to build the sequence of the first n Fibonacci numbers and then take the last one. The case of 0 is a special one, and is handled specially

{ fib (eq@[id,%0] -> %0; last@fibs) }

Now, how does the helper function work? It will compute the first n Fibonacci numbers by computing the first (n-1) Fibonacci numbers and then adding the last two. The base cases are n=1 (in which case we just use <1>) and n=2 (in which case we just use <1,1>).

{ fibs (eq@[id,%1] -> %<1> ;
(eq@[id,%2] -> %<1,1> ;

Now, what about the recursive case? We need to

• do the recursive call, computing recursive
fibs@-@[id,%1]

• add the last two elements, computing newval
+@[-1,-2]

• and shove the new value on the end
apndr@[recursive,newval]

putting it all together, we get
{ fibs (eq@[id,%1] -> %<1> ;
(eq@[id,%2] -> %<1,1> ;
apndr@[id,+@[-1,-2]]@fibs@-@[id,%1])) }

### 4. Extending FP

As many of you have noted, FP uses a somewhat different technique for describing the semantics of built-in functions and functional forms. In this question, you will further explore that technique.

Suppose we decided to add a case functional form to FP. Case has the form

case p f1 f2 ... fn

This is a true functional form. All arguments are functions, and it creates a new function. The informal meaning of case is ``apply p to the argument; if the result is 1, apply f1 to the argument; if the result is 2, apply f2; and so on and so forth''.

Write a formal definition of this new case functional form, using the same notation and care that Backus uses.

History

• Created Wednesday, March 10, 1999.
• A few minor changes on Friday, March 12, 1999.
• A few more details on Monday, May 17, 1999.

Disclaimer Often, these pages were created ``on the fly'' with little, if any, proofreading. Any or all of the information on the pages may be incorrect. Please contact me if you notice errors.