Principles of Programming Languages — Homework 12

Due by class time Tuesday, November 2

Reading

Exercises

  1. In class, we added support to our interpreter for function expressions of any number of parameters, and application expressions with any number of arguments. For example, the expression (function (x y z) : (x + (y + z))) creates a function with three parameters that returns the sum x+y+z. If we give this function the name f, the application expression (f 1 2 3) will call the function on the arguments 1, 2, and 3, and the value 6 will be returned:

       (run '(with [f = (function (x y z) : (x + (y + z)))] :
                (f 1 2 3)))
        → 6
    
    If we call the function on more than three arguments, for example: (f 1 2 3 4 5), it will also work and we will get the same result of 6. However, this is not really desirable behavior, because passing an incorrect number of arguments to a function should generate an error message. The function should first check to make sure that the number of arguments passed into it matches the number of function parameters, and only execute the body expression if the right number of arguments was provided.

    Modify the interpreter so that if a function is passed an incorrect number of arguments, it will generate the error message "wrong number of arguments received". The same goes for the primitive functions square, squareRoot, minus, !, and average in the initial environment. Examples:

    ;; should return 6
    (run '(with [f = (function (x y z) : (x + (y + z)))] :
             (f 1 2 3)))
    
    ;; should generate error: wrong number of arguments received
    (run '(with [f = (function (x y z) : (x + (y + z)))] :
             (f 1 2 3 4 5)))
    
    ;; should return 25
    (run '(square 5))
    
    ;; should generate error: wrong number of arguments received
    (run '(square 1 2 3))
    
    ;; should return 6
    (run '(average 5 7))
    
    ;; should generate error: wrong number of arguments received
    (run '(average 1))
    

  2. Our current syntax for application expressions is:

      (<var> <exp> ...)
    
    where "<exp> ..." means "any number of <exp> expressions". For example: (f 1 2 3). But the <var> expression, called the operator, must be a variable, such as f. However, what if we want to use a more complex operator expression in place of a variable, as in the following code?
    (with [choice = 1] :
      ((if (choice == 1) then square else squareRoot) 9))
    
    would return 81 (the square of 9), whereas:
    (with [choice = 2] :
      ((if (choice == 1) then square else squareRoot) 9))
    
    would return 3 (the square root of 9).

    Modify the interpreter to support the more general application syntax:

      (<exp> <exp> ...)
    
    so that the operator is no longer restricted to being just a variable, but can now be any arbitrary expression. Hint: you will need to modify your interpreter in two places, but very little new code is necessary. Here are a few examples you can use for testing, which you can copy and paste directly into DrRacket:
    ;; should return 81
    (run '(with [choice = 1] :
            ((if (choice == 1) then square else squareRoot) 9)))
    
    ;; should return 3
    (run '(with [choice = 2] :
            ((if (choice == 1) then square else squareRoot) 9)))
    
    ;; should return 120
    (run '((if True then ! else minus) 5))
    
    ;; should return -5
    (run '((if False then ! else minus) 5))
    
    ;; should return 42
    (run '((function (a) : a) 42))
    
    ;; should return 6
    (run '((function (x y z) : (x + (y + z))) 1 2 3))
    
    ;; should return 25
    (run '((with [f = square] : f) 5))
    


Turning in Your Homework