Principles of Programming Languages, Spring 2016 Assignment 6

Submission Instructions 1. Submit a zipped file named id1_id2.zip where id1 and id2 are the IDs of the students responsible for the submission (or id1.zip for one student in the group). 2. A contract must be included for every implemented procedure. 3. Use exact procedure and file names, as your code is tested automatically. 4. Answer theoretical questions in a pdf file named ex6.pdf

Imperative Programming: 1. Mutable data and local state. 2. Environment-based interpreter and for .

Question 1: Theoretical Questions: 1. Can the substitution model support imperative programming? Explain your answer. 2. Is referential transparency a property of imperative programming? Justify your answer with an example. 3. Give an example for a constructor and a mutator in Scheme. What is the difference in the functionality between constructors and mutators? 4. Is the special operator begin useful in pure (i.e. without any kind of side effects)? Explain. 5. Explain the differences between the evaluation of a definition expression and an assignment expression in our interpreter.

1 Question 2: Implementing a Queue in Scheme A queue is a particular kind of abstract data type or collection in which the entities in the collection are kept in order and the principal (or only) operations on the collection are the addition of entities to the rear terminal position, known as enqueue, and removal of entities from the front terminal position, known as dequeue.

This makes the queue a First-In-First-Out (FIFO) data structure. In a FIFO data structure, the first element added to the queue will be the first one to be removed. This is equivalent to the requirement that once a new element is added, all elements that were added before have to be removed before the new element can be removed. Often a front operation is also included, returning the value of the front element without dequeuing it. [Wikipedia].

In this question you are required to implement a bounded queue in Scheme. The implementation should be a lazy-procedural one, and use imperative programming, meaning that it should have a local state that will be changed according to the mutators. No tagged data is required.

You are required to implement the following interface for the bounded queue ADT in the file q2.rkt (included within the submission template):

● A value constructor, make-bounded-queue, which receives the bound on the number of elements in the queue and returns an empty queue:

; Signature: make-bounded-queue(n) ; Type: [Number -> Queue(T)] ; Purpose: Constructor of a bounded queue ; Precondition: 0 < n

● Selector dequeue to return the front element from the queue, provided it is not empty, and remove it from the queue (i.e. it is also a mutator):

; Signature: dequeue(queue) ; Type: [Queue(T) -> T] ; Purpose: Removes the element on the front of a non-empty queue ; and returns it. ; Precondition: queue is not empty ; Tests: (let ((my-queue (make-bounded-queue 10))) ; (enqueue my-queue 4) ; (enqueue my-queue 5) ; (dequeue my-queue) ; (enqueue my-queue 6) ; (dequeue my-queue)) => 5 ; (dequeue my-queue)) => 6

2 ● Mutator enqueue that adds a new element to the back of the queue (provided the queue is not full):

; Signature: enqueue(queue, elt) ; Type: [Queue(T) * T -> Void] ; Purpose: Adds an element to the back of a non-full queue. ; Precondition: queue is not full ; Tests: see above

Question 3:

Memoization is a technique that enables a procedure to record, in a local store, values that have previously been computed. This technique can make a vast difference in the performance of a program. A memoized procedure maintains a store in which values of previous calls are stored using as keys the arguments that produced the values. When the memoized procedure is asked to compute a value, it first checks its storage to see if the value is already there and, if so, just returns that value. Otherwise, it computes the new value in the ordinary way and stores this in the table. a. Write a procedure memoize that receives a procedure f, and produces a procedure memo-f which is a memoized variant of f: ;signature: memoize(f) ;purpose: Create a memoized version of a 1-parameter procedure f ;type: [ [T1->T2] -> [T1->T2]] ;example: > (define memo-first-first (memoize (lambda (lst) (display lst) (newline) (first (first lst)))) )) > ( memo-first-first '((1 2) (3 4)) ) '((1 2) (3 4)) 1 > ( memo-first-first '((1 2) (3 4)) ) 1

You are encouraged to use the queue from the previous question to implement a memoization table. For this purpose you may assume that a memoized procedure will be applied to at most 100 different parameters. Write your solution in the file q3.rkt.

3 b. Apply the memoize procedure to a recursive procedure like Fibonacci: > (define fib (lambda n) (display n) (cond ((= n 0) 0) ((= n 1) 1) (else (+ (fib (- n 1)) (fib (- n 2)))))) > (define memo-fib (memoize fib))

Run: > (memo-fib 3) 3 2 1 0 1 2 There is a problem! Use an environment diagram to explain the problem. Add your answer in ex6.pdf. . Correct the memoization of fib (write the corrected solution in ex6.pdf). Explain, using an environment diagram how the corrected memoization produces a truly memoized fib procedure. Suggest a general rule for memoization of recursive procedures like fib. Write your solution in the file ex6.pdf.

Question 4: Supporting Memoized Procedures:

Recall the memoization definition from the previous question. Make the required changes in the modules asp.rkt, env-ds.rkt and interpreter-core.rkt (included under the folder q4 within the submission template), in order to support memoized procedures. In addition, the interpreter needs to support the following operation:

(proc-query ) where is the name of a memoized-procedure. The operation should return a key-value association list. Each key is a list of arguments to which the memoized procedure has been applied. Each value is the result stored for the corresponding arguments. In case is not a memoized-procedure, the operation should return an error.

4 Examples: > (derive-eval '(define fact (lambda-memo(n) (display n) (newline) (if (= n 0) 1 (* n (fact (- n 1)))) ))) 'ok > (derive-eval '(proc-query fact)) '() > (derive-eval '(define g fact)) 'ok > (derive-eval '(fact 2)) 2 1 0 2 > (derive-eval '(g 3)) 3 6 > (derive-eval '(proc-query fact)) '(((3) . 6) ((2) . 2) ((1) . 1) ((0) . 1))

Question 5: Axiomatic Type Inference:

Add your answers for the questions below in the file ex6.pdf:

a. Derive a type for the expressions below.

(define x 0) ; expression 1 (set! x (+ x 1)) ; expression 2 (set! x (begin (set! x 'a) 2)) ; expression 3

For each step indicate: 1. which Rule is used, 2. the base typing-statements, 3. the unifier applied to the typing statements in the rule condition, 4. Whether Monotonicity was used.

For each of the expressions 1-3, state whether it is well-typed and note the type for the given expression if exists.

5 b. Recall the inference rule for set! The type of '(set! x e) is always Void. Why is there a condition that e has a type _S and x has the same type _S for some type expression _S? c. Suppose that the set! rule is relaxed, to enable: e has type _S1 and x has type _S2. for some type expressions _S1 and _S2. What is the change, compared to the typing rule mentioned above. Give an example. d. Which rule is preferable?

6