For this assignment, you will modify Paret to use call-by-need (i.e. lazy) evaluation, and also to have pairs.

You will add pairs to the language. `(pair fst snd)`

constructs a pair with two elements, `fst`

and
`snd`

. `(first p)`

gets the first element
of a pair `p`

, and `(second p)`

gets the
second element. If the argument of `first`

or `second`

is not a pair, you should raise `err-not-a-pair`

with the value. `(is-pair v)`

returns true if `v`

is a pair value, and false if `v`

is other kinds of value.

Lazy evaluation means that arguments to functions should not be evaluated if they are not
used in the body. As a result, many places your interpreter that used to work
with values, will instead work with `Computation`

s:

```
data Computation:
| c-suspend(body :: Expr, env :: Env)
| c-value(value :: Value)
end
```

A `Computation`

is either a `c-suspend`

,
which represents an argument that has
not yet been evaluated, or a `c-value`

, which has.

Function calls and the `pair`

constructor must be
lazy: that is, a function
must not evaluate its argument if it is not used, and a pair must not
evaluate its parts if they are not used. `let`

's value should
also be lazy, and you should make sure that desugaring will respect this.

On the other hand, other primitives in the language should force their arguments to be
evaluated. Specifically, operators should force their arguments to be evaluated, `if`

should force its condition
to be evaluated, and `first`

and `second`

should force the accessed element of the pair to be
evaluated. Likewise, the top-level
interpreter should force its result to be evaluated (This is why `eval`

, in
the code stencil, returns a `Value`

rather than a
`Computation`

). All evaluations should be shallow. That is, it should not recursively force values inside.

For efficiency, when a computation is forced, its result should be cached so
that it doesn't have to be computed a second time. We have done this for you
in the code stencil with the `force`

function. It uses structural mutation to
make sure that a computation is only performed once (`ref`

marks a field as
mutable in Pyret):

```
data RefComputation:
| r-comp(ref comp :: Computation)
end
```

```
<expr> ::= <num>
| true
| false
| <string>
| (+ <expr> <expr>)
| (++ <expr> <expr>)
| (num= <expr> <expr>)
| (str= <expr> <expr>)
| (if <expr> <expr> <expr>)
| (and <expr> <expr>)
| (or <expr> <expr>)
| (let (<id> <expr>) <expr>)
| <id>
| (lam <id> <expr>)
| (<expr> <expr>)
| (pair <expr> <expr>)
| (first <expr>)
| (second <expr>)
| (is-pair <expr>)
```

```
type Env = StringDict<RefComputation>
data Value:
| v-num(value :: Number)
| v-str(value :: String)
| v-bool(value :: Boolean)
| v-fun(param :: String, body :: Expr, env :: Env)
| v-pair(first :: RefComputation, second :: RefComputation)
end
data Computation:
| c-suspend(body :: Expr, env :: Env)
| c-value(value :: Value)
end
data RefComputation:
| r-comp(ref comp :: Computation)
end
data Expr:
| e-num(value :: Number)
| e-str(value :: String)
| e-bool(value :: Boolean)
| e-op(op :: Operator, left :: Expr, right :: Expr)
| e-if(cond :: Expr, consq :: Expr, altern :: Expr)
| e-lam(param :: String, body :: Expr)
| e-app(func :: Expr, arg :: Expr)
| e-id(name :: String)
| e-pair(fst :: Expr, snd :: Expr)
| e-first(pair :: Expr)
| e-second(pair :: Expr)
| e-is-pair(expr :: Expr)
| sugar-and(left :: Expr, right :: Expr)
| sugar-or(left :: Expr, right :: Expr)
| sugar-let(name :: String, expr :: Expr, body :: Expr)
end
data Operator:
| op-plus
| op-append
| op-str-eq
| op-num-eq
end
data InterpError:
| err-if-got-non-boolean(val :: Value)
| err-bad-arg-to-op(op :: Operator, val :: Value)
| err-unbound-id(name :: String)
| err-not-a-function(val :: Value)
| err-not-a-pair(val :: Value)
end
```

(For reference, feel free to look at the definitions file.)

To get started, you can open the code stencil and the
test stencil in `code.pyret.org`

.

Please read the test guidelines

Like closure, programs can evaluate to a pair value. Since your implementation affects the
representation of pair values, in your test submission you should only test
whether the program returns *a* pair, not which specific pair it
returned. Likewise, if you're testing for an exception, make sure not to
test that it contains a *specific* pair value.

On the other hand, in your implementation file, you are free to test whether
the program returns a specific pair or a specific function. Note however that these values contain
`RefComputation`

s which are mutable, so you have to use `is=~`

instead of `is`

. See more details here.