CS 360

Winter 2018

Programming Language Concepts

**CS 360-001** Tuesday/Thursday 15:30-16:50 (Rush 209)

**CS 360-002** Tuesday/Thursday 14:00-15:20 (Rush 209)

**CS 360-003** Tuesday 18:30-21:20 (UCross 151)

Instructor: |
Geoffrey Mainland mainland@drexel.edu Office: University Crossings 106 Office hours: Mondays 4pm–7pm; Thursdays 5pm–6pm. |

Teaching Assistant: |
Xiao Han CLC office hours: Tuesday 12pm–2pm; Thursday 6pm–8pm Allen Yang CLC office hours: Wednesday 6pm–8pm |

Due Friday, March 9, 11:59:59PM EST.

Accept this assignment on GitHub Classroom here. The standard homework instructions apply.

In this assignment, you will complete the implementation of MiniScheme we saw in lecture.

The rules for evaluating MiniScheme are a formalized version of the environment model of evaluation we saw at the beginning of the term. Although MiniScheme’s syntax looks much like Scheme’s, it’s abstract syntax is represented by an algebraic data type instead of by lists of atoms.

You should have read Chapters 12 and 13 of LYAH before beginning this homework.

Hoogle may be useful for looking up functions from the standard library.

You must complete the implementations of `evalDecl`

and `eval`

in the file `src/MiniScheme/Eval.hs`

. The evaluator can be used in any monad that is an instance of the `MonadEval`

type class, which is defined in the same file. Any monad that has the proper type of environment and state, that can raise exceptions, and that perform IO can be an instance of `MonadEval`

—that is all it takes to be an “evaluation monad.” We have provided two such monads: one that provides for deterministic evaluation, and one that provides full support for non-deterministic evaluation with `amb`

. You don’t have to change any code to get full support for `amb`

—you just have to choose the right evaluation monad.

The function `eval`

evaluates expressions, of type `Exp`

, to produce values, of type `Val`

. The function `evalDecl`

evaluates declarations, like `(define x 3)`

, which bring new variables into scope.

`evalDecl`

is tricky to understand; here is its type:

```
evalDecl :: MonadEval m => Decl -> (Maybe Val -> m a) -> m a
```

Its second argument is a continuation, which takes the value of the declaration and performs some additional computation. Declarations may or may not produce a value, which is why the argument of the continuation has the type `Maybe Val`

. Any variable brought into scope by the declaration should be visible **only** in the continuation.

This assignment is worth 50 points. There are 65 possible points.

There are two ways you can interact with the MiniScheme interpreter: via a REPL that evaluates MiniScheme expressions as you type them in, or via direct calls to the evaluator using `ghci`

.

After typing `make`

, the MiniScheme interpreter will be available as `bin/minischeme`

. You may invoke it three ways:

`minischeme [FILE...]`

will evaluate the files given as arguments using a deterministic evaluation monad.`minischeme --amb [FILE...]`

will evaluate the files given as arguments using a**non**-deterministic evaluation monad.`minischeme -i`

or`minischeme --interactive`

will start the interactive REPL, which uses the deterministic evaluation monad.

We have provided several example programs in the `examples`

directory, all of which can be run with the `minischeme`

interpreter. The `puzzle.scm`

example requires full support for `amb`

. It can be run as follows:

```
./bin/minischeme --amb examples/puzzle.scm
```

The REPL will try to parse whatever you throw at it, which can span multiple lines. If it gets stuck parsing something illegal, you can type `Ctrl-C` get back to the REPL prompt. To exit the interpreter, type `:quit` or `Ctrl-D` at the REPL prompt.

`ghci`

To use `ghci`

, type `stack repl`

. This will start `ghci`

and load all of the modules for the assignment. There are several helper functions that will be useful during debugging:

`parseExp :: String -> Maybe Exp`

will parse an expression.`run :: String -> IO (Either String [Val])`

will parse a MiniScheme program and run it using the deterministic evaluation monad.`runAmb :: String -> IO (Either String [[Val]])`

will parse a MiniScheme program and run it using the**non**-deterministic evaluation monad.

`let`

(10 points)Complete the implementation of `let`

in the function `eval`

.

Hint: Use the `extendVars`

function to create a local environment in which to evaluate the body of the `let`

. You will also want to use `mapM`

to evaluate the expressions that give the initial values of the let-bound variables. Here is its type signature:

```
mapM :: Monad m => (a -> m b) -> [a] -> m [b]
```

`unzip`

will be useful to split apart the let-bound variables and the expressions that give their initial values.

`amb`

(10 points)Add support for `amb`

in the interpreter. This requires a single line of code. Make sure you read about `MonadPlus`

in Chapter 12 of LYAH.

Hint: You should use the function `msum`

from the standard library. You **should not** need `mapM`

here, but you will probably need `map`

! Here is the type of `msum`

```
msum :: MonadPlus m => [m a] -> m a
```

`lambda`

(10 points)Implement support for `lambda`

expressions. The value of a lambda expression should be a closure. The `ClosV`

data constructor in the `Val`

data type is used to represent a closure. You will need to get the code part of the closure directly from the lambda expression. Where can you get the current environment? Just use `ask`

, which has this type:

```
ask :: MonadReader r m => m r
```

Finish the definition of `apply`

to support calling user-defined procedures. You will need to extend the environment in the closure with bindings for the parameters. Just as with `let`

, you will want to use `extendVars`

.

Hint: Use `local`

to swap out the current environment for the environment in the closure when you evaluate the body of the lambda. It has this type:

```
local :: MonadReader r m => (r -> r) -> m a -> m a
```

How can you provide a proper argument of type `r -> r`

to `local`

? You should provide a function that ignores its argument and simply returns the environment contained in the closure. The function `const`

will probably be useful!

`define`

(20 points)There are two separate forms of `define`

in MiniScheme: `(define v e)`

and `(define (f param ...) body)`

. The latter **is not** just syntactic sugar for the former since it must permit recursive function definitions—see the big-step rules for declarations from lecture. Neither form of `define`

produces a value, so the continuation of `evalDecl`

should be called with `Nothing`

as an argument in both cases.

Hint: For the `DefineLamD`

case, you will want to explicitly allocate a store location with `alloc`

and then use `extendVarLoc`

to bind the defined function’s name to this location. `extendVarLoc`

has this type:

```
extendVarLoc :: MonadReader Env m => Var -> Loc -> m a -> m a
```

The third argument is its continuation. This continuation should create a closure using the current environment, which now has a binding for the named function. The easiest way to do this is to create a lambda expression using the `LamE`

data constructor and recursively call `eval`

. When you have the value of this lambda expression, set the location to point to this value using `setVal`

. Then invoke `evalDecl`

’s continuation `k`

! This part of the problem is hard!

How long did spend on each problem? Please tell us in your `README.md`

. You may enter time using any format described here.