A5: FAQ

My code for eval_expr is getting long and ugly with deeply-nested pattern matches. I tried factoring out helper functions, but I couldn’t because the all need eval_expr. How can I clean up my code?

Try splitting out the code into mutually-recursive helper functions. In OCaml you can write let rec f = fun ... and g = fun ... for mutually recursive functions f and g.

Why does make test fail as unimplemented after I’ve implmented some basic expressions?

Some of the functions used at the top level in main.ml invoke helper functions for building and maintaining the dynamic environment. Try providing implementations for prepend_env and update_env. The error may also be coming from string_of_env or eval_prog depending on what you are trying to do.

How should I implement the env type?

Your implementation doesn’t need to be complicated. Recall the types you used for dictionaries in A4. It is not necessary to enforce any strict representation invariants for the environment. If update_env and find_env are more than a line or two, you might be overthinking things a bit.

Where is the implementation of print and println supposed to be? How do I add closures for them to the initial environment?

Your interpreter will need to provide OCaml implementations of the print and println functions. This means you won’t be able to use a normal closure value, with an RML expression inside. Instead, you might consider adding constructors to the value type for your printing functions. Of course, you will also need to handle these values in the code for evaluating function application.

What are these pattern things?

Patterns are a completely separate syntactic construct. For instance, [] as a pattern is distinct from [] as an expression and [] as a value. Patterns are primarily used in pattern matching expressions, but they can also be used in let-expressions and anonymous functions.

What is this bind_pattern function, and what am I supposed to use it for?

This function implements the run-time behavior of patterns. To evaluate a pattern against a value, we first check whether their structure “matches” each other. If they do match, then we construct a new environment with mappings for each variable bound in the pattern. If they do not match, then bind_pattern should return None. The precise definitions of which patterns match which values, and which bindings are created for a successful match, are given in the formal semantics.

How is testing supposed to work?

The release code provides three ways to run RML programs.

The first way is through test.ml and make test. You can use and extend the test suite in test.ml by calling make_test on a string containing an RML expression and string representation of the expected value. You are also free to implement a new test-maker function for definitions if you wish, though we recommend testing definitions with one of the other two methods.

The second is through the shell. You can start an interactive session with your RML interpreter by calling make repl to enter a utop-like shell for RML. The shell accepts both expressions and definitions, and support a few useful commands that you can use to make sure things are running smoothly.

The third is through the make run command, which will prompt you for an RML program you wish to run in its entirety. You may implement complete RML programs (lists of definitions, as in OCaml) in separate files with the .rml extension. These should be placed in the empty rml/test directory to keep things neat.

How do I go about testing the imperative expressions?

It will be difficult (and kind of strange) to test individual imperative expressions in isolation, since they are mostly used to produce side effects. For instance, for references, it makes sense to develop tests that combine multiple expressions in interesting ways (e.g., create a reference to an integer, increment it, and then dereference it) and verify the final value.

How do I go about testing the concurrent expressions?

Again, testing concurrent expressions in isolation doesn’t make very much sense. In fact, it will be tricky to test any of these expressions until you have a reasonable implementation of all of them. Try spawning some robots that communicate with their parent robots using send and recv.

How do I test print and println?

These two functions are primarily for your own use in debugging, so you don’t need to worry too much about testing them thoroughly. Note that since they both write to stdout, testing them via make test or make repl will be difficult, since their output will be interleaved with other print statement and won’t be very readable.