CS558 Homework #5
Due 4:00 PM, Tuesday, February 18, 2014
Although HW #5 is not due until the 18th, the material it
covers is fair game for the mid term exam on Feb. 11 2014.

Homework must be submitted via D2L. All submitted files (3 for this assignment sol5A.e5, hw5A.hs, and sol5B.hs) must be submitted in the appropriate D2L directory in the drop box HW5.It is your responsibility to submit the homework in the proper format with the proper names.

All programs mentioned can be downloaded from the this document

This homework has two parts. First, altering the definitional interpreter for a small functional language, and Second, writing a few small programs in a functional language (Haskell).

  1. Consider the following functional language, which we'll call E5.
    prog := exp
    exp := var
    | int
    | char
    | '(' '=' exp exp ')'
    | '(' 'if' exp exp exp ')'
    | '(' 'let' var exp exp ')'
    | '(' 'letfun' var var exp exp ')'
    | '(' '@' exp exp ')'
    | '(' '+' exp exp ')'
    | '(' '-' exp exp ')'
    | '(' '*' exp exp ')'
    | '(' '/' exp exp ')'
    | '(' '<=' exp exp ')'
    | '(' 'pair' exp exp ')'
    | '(' 'fst' exp ')'
    | '(' 'snd' exp ')'
    | '(' 'ispair' exp ')'
    | '(' 'ischar' exp ')'
    | '(' 'ispair' exp ')'
    | '(' 'isint' exp ')'
    
    var := letter { letter | digit }
    

    As usual, comments may be included by enclosing them between '{' and '}' characters, and they may be nested.

    E5 is similar to some of our earlier languages (especially E4), it retains characters and their operations, but lacks imperative features including assignment, while , block , and write. It The local expression has been renamed let (to emphasize that it acts like an Haskell-style immutable binding), and only one variable can be defined (nested let's can be used to define multiple variables).

    The most important changes are in the treatment of functions, which are now defined using a locally-scoped letfun expression form. Functions are treated as just another kind of value, and they share the same name space as other values. To evaluate (letfun f x b e ) , first create a function value whose formal argument is x , whose body is b , and whose environment is the current environment; then bind f to that function value and evaluate e in the resulting environment. Applications now take an arbitrary expression in the function position; this must evaluate to a function value. All functions take exactly one argument; pairs can be used to encode multiple arguments (or multiple results). Functions are completely "first-class", i.e., they can be passed as arguments to, or returned as results of, other functions, and can be stored in pairs. Since there is now no need for a separate (fun ...) declaration form, a program is once again just an expression (most probably with some outer let or letfuns defining things that will be known globally).

    The web site gives several example E5 programs. Program static.e5 illustrates that nested functions use static scoping rules; program compose.e5 shows how to write a higher-order function that composes two existing functions.

    An E5 interpreter in Haskell (only) has been provided ( hw5.hs ).

    In the E5 language there are exactly 4 kinds of values. Ints, Characters, Pairs, and Functions. There are 3 predicates on values: ispair, ischar, and isint. With these you can test what kind of value any object takes. In addition, the interpreter for E5 attempts to recognize the convention we have used to encode lists. Recall that convention, it uses right-nested pairs ending in 0. For example (pair 4 (pair 'z' 0)) evaluates to (4.('z'.0)). In the interpreter for E5 if such a right-nested pair is recognized it is printed as [4,'z']. The interpreter also recognizes right-nested pairs of characters, and prints them as strings. For example (pair 'a' (pair 'b' (pair 'c' 0))) prints as "abc". There are still only 4 kinds of values, though we print some of them using the list conventions. Here are some examples

    1. (pair 4 (pair 5 (pair 9 0))) prints as [4,5,9] .
    2. (pair 3 (pair 5 6)) print as (3.(5.6)). Note that while it is right nested, it does not end in 0, so the list form is not used.
    3. (pair 'd' (pair 'z' (pair '3' 0))) print as "dz3".
    4. (pair 'a' (pair 'b' (pair 5 0))) prints as ['a','b',5]. Note that is starts out looking like a list of Character, but the last element is not a Character, so we can't print it as a string. But it stills looks like a list.

    What to do.

    You will need to write some small E5 programs. You will also modify the definitional interpreter. You might try adding the list convention functions to an E5 program. While the idea is the same, the format of definitions is very different in E5 than in E3 and E4. This would be good practice before you start.

    Note that this interpreter has some significant changes from the previous one for E4. We no longer use a stack, just an environment and a heap. There is a (single) environment (because both functions and other values can now be stored in the same name space). The environment now maps all identifiers directly to addresses in the heap. But, we use addresses only when we allocate new cells in the heap, We never change values in the heap since there is no assignment (or call by reference).

  2. Implement the following functions in Haskell using the Haskell function foldr, without using explicit recursion. Submit your solutions to both the exercises in a single file sol5B.hs.