Exercises

Course links

1. Add a multiplication operation to the AE programming language. Rewrite the definition of the AE data type and the parser to implement this change.

2. Add a unary negation operation (i.e., one that computes the additive inverse of the value of its operand) to the AE programming language. Rewrite the type definition and the parser to implement this change.

3. In exercise 2, the structure of the parser that you wrote depends on whether you chose to use the same operator symbol for both subtraction and unary negation (as in, for instance, Java), or different ones (as in, for instance, the ML programming language, which uses ~, the tilde, for unary negation and -, the hyphen, for subtraction). Describe and explain this structural difference between the parsers. Does it affect the abstract syntax of AEs in your extended language? If so, how?

4. The with-expression in the WAE programming language is somewhat limited, in that it allows the programmer to bind only one identifier. Implement a programming language MWAE, exactly like WAE except that its with-expression permits the binding of zero or more identifiers, all having the same scope.

Here's an extended BNF giving the concrete syntax of this language:

<MWAE> ::= <numeral>
         | "{" "+" <MWAE> <MWAE> "}"
         | "{" "-" <MWAE> <MWAE> "}"
         | "{" "with" "{" {"{" <identifier> <MWAE> "}"}* "}" <MWAE> "}"
         | <identifier>

Implementing this language entails (1) writing a type definition for the MWAE type that expresses the abstract syntax, (2) writing a parser, and (3) writing an interpreter. I recommend using a substitution-based evaluator (similar to subst and calc for WAE).

5. Implement the programming language MFAE, exactly like FAE except that its fun-expression allows functions of any (fixed) number of arguments and its application expressions can have any number of operands.

Here's an extended BNF giving the concrete syntax of this language:

<MFAE> ::= <numeral>
         | "{" "+" <MFAE> <MFAE> "}"
         | <identifier>
         | "{" "fun" "{" {<identifier>}* "}" <MFAE> "}"
         | "{" {<MFAE>}+ "}"

The interpreter should, of course, signal an error when the number of operands in an application does not match the number of arguments that the function being invoked expects.

Include in your implementation a preprocessor for multi-binding with-expressions, with the syntax suggested in exercise 4. The preprocessor should convert them into equivalent application expressions in which the operand is a fun-expression. (The number of arguments that the function takes is the same as the number of bindings in the with-expression.)

6. Extend MFAE to include subtraction, multiplication, and an if0 conditional with the concrete syntax

<MFAE> ::= "{" "if0" <MFAE> <MFAE> <MFAE> "}"

and the semantics that the value of the entire expression is the value of the second <MFAE> when the value of the first <MFAE> is zero and the value of the third <MFAE> when the value of the first <MFAE> is non-zero. Run your extended MFAE on the program

{with {{make-factorial {fun {maker n}
                         {if0 n
                              1
                              {* n {maker maker {- n 1}}}}}}}
  {with {{factorial {fun {n}
                      {make-factorial make-factorial n}}}}
    {factorial 5}}}

Is either of the functions denoted by the fun-expressions recursive? If so, explain how this semantics is implemented; if not, explain how the illusion of recursive definition is achieved.

7. Extend BCFAE to include the following features:

Here's the concrete syntax for the proposed language:

<PCFAER> ::= <number>
           | <identifier>
           | "{" "+" <PCFAER> <PCFAER> "}"
           | "{" "-" <PCFAER> <PCFAER> "}"
           | "{" "*" <PCFAER> <PCFAER> "}"
           | "{" "with" "{" <identifier> <PCFAER> "}" <PCFAER> "}"
           | "{" <PCFAER> <PCFAER> "}"
           | "{" "fun" "{" <identifier> "}" <PCFAER> "}"
           | "{" "if0" <PCFAER> <PCFAER> <PCFAER> "}"
           | "{" "rec" "{" <identifier> <PCFAER> "}" <PCFAER> "}"
           | "{" "seqn" {<PCFAER>}+ "}"
           | "{" "newpair" <PCFAER> <PCFAER> "}"
           | "{" "setcar" <PCFAER> <PCFAER> "}"
           | "{" "setcdr" <PCFAER> <PCFAER> "}"
           | "{" "opencar" <PCFAER> "}"
           | "{" "opencdr" <PCFAER> "}"
           | "nil"
           | "{" "ifnil" <PCFAER> <PCFAER> <PCFAER> "}"

Submit (a) an type definition for PCFAER that captures its abstract syntax; (b) a pre-processor that converts with-expressions into semantically equivalent applications before parsing; (c) a parser; (d) an interpreter; (e) other type definitions and helper functions as appropriate; and (f) test cases, in sufficiently large number and variety to establish that all of the features work correctly and harmoniously.

You may find that you have to make some decisions about the semantics of the various constructions that are not immediately obvious. Document any such decisions carefully.

8. Here's a Web application that provides the user with several potential addends, one at a time, and allows the user to select or deselect each of them (by typing "1" or "0"). It then displays the sum of the selected addends.

(define (select n)
  (web-read
    (format "The candidate is ~a.  Add to total? (1 for yes, 0 for no): "
            n)))

(define (filter0 predicate ls)
  (if (empty? ls)
      empty
      (if (zero? (predicate (car ls)))
          (filter0 predicate (cdr ls))
          (cons (car ls) (filter0 predicate (cdr ls))))))

(define (sum ls)
  (if (empty? ls)
      0
      (+ (car ls) (sum (cdr ls)))))

(web-display (sum (filter0 select (list 1 6 28 496 8128))))

Transform this, using the methods developed in chapters 16 and 17 of our textbook, so that it uses web-read/k and can execute (in principle) on a traditional Web server. (In other words, you need to submit the transformed Scheme code, but you don't need to provide the actual Web pages that would implement it.)

9. Write an operational semantics for the PCFAER language described in exercise 7 above. Since the evaluation of a PCFAER expression requires both an environment and a store and yields both a value and a store, you'll need to formulate semantic rules with an expression, an environment, and a store on the left of the double-shafted arrow and a value and a revised store on the right. For instance, the rule for simple identifiers would be

i, E, S => S(E(i)), S

That is: evaluating i in environment E with store S yields the result of looking up i in E (to get a location), then looking up that result in S (to get a value), with no effect on the store.

10 (revised). In adding conditionals and recursion to TFAE, supppose that, instead of rec-expressions, we introduce a kind of expression more similar in form to Scheme's named let-expressions, so that the computation of 5! looks like this:

{namedlet loop : (number -> number) {n 5}
  {if0 n
       1
       {* n {loop {- n 1}}}}}

The BNF for namedlet-expressions in the resulting language TNLCFAE ("types + namedlet-expressions + conditionals + functions + arithmetic expressions") would be

"{" "namedlet" <identifier> ":" <type> "{" <identifier> <TNLCFAE> "}" <TNLCFAE> "}"

First, write a type rule for namedlet-expressions and a semantic rule for their interpretation in TNLCFAE. Then revise the language processor that we developed in class for TRCFAE by removing rec-expressions and putting in namedlet-expressions instead.

11. Implement Hindley-Milner type inference for the language NLCFAE, which is similar to the TNLCFAE language from exercise 10, but without the type annotations. Here's the BNF for the language I have in mind:

<NLCFAE> ::= <numeral>
           | <identifier>
           | "{" "+" <NLCFAE> <NLCFAE> "}"
           | "{" "-" <NLCFAE> <NLCFAE> "}"
           | "{" "*" <NLCFAE> <NLCFAE> "}"
           | "{" "if0" <NLCFAE> <NLCFAE> <NLCFAE> "}"
           | "{" "fun" "{" <identifier> "}" <NLCFAE> "}"
           | "{" <NLCFAE> <NLCFAE> "}"
           | "{" "namedlet" <identifier> "{" <identifier> <NLCFAE> "}" <NLCFAE> "}"

Your implementation should take the form of a procedure that takes an NLCFAE abstract-syntax tree and attempts to infer a type for each subexpression in that tree that is rooted at an NLCFAE node. If all of the subexpressions can be consistently typed, your procedure should return the substitution that records all of its inferences; if not, it should return #f.

Hint: Start by writing PLAI type definitions for NLCFAE-Type, TypeConstraint, and Substitution.