CS691F Programming Languages

| Introduction | Schedule | Software | OCaml Standard Library | Course Libraries |

Continuations and the Web

Due date: Wednesday, September 27th, 11:59PM

You must update the course software to get the support code for this assignment. From the terminal:

$ opam update
$ opam intall cs691f.1.5.0

Introduction

This assignment has two parts. In the first part, you will implement an evaluator for a simple language that makes continuations explicit. In the second part, you modify your evaluator so that it can run on the Web.

Requirements

Submit a file named Eval.ml with the following signature.

(* Required interface *)
type context
val empty_context : context
val is_empty_context : context -> bool

val is_value : M_syntax.exp -> bool

val step : M_syntax.exp -> context -> M_syntax.exp * context

(** [serve port exp] *) 
val serve : int -> M_syntax.exp -> unit

As the signature suggests, you need to define the structure of control strings yourself. We provide the module M_syntax , which defines the abstract syntax of the language you must use.

The provided M_util module defines parsers and pretty-printers that you'll need to implement the read and write expressions. You may assume that the user will never try to read or write a function or a data structure that contains a function. (But, feel free to support reading and writing functions if you wish.)

Directions (Part 1)

We recommend hacking up your solution incrementally. First, follow all the steps below just for binary operators and constants, then for data-structures, then functions, and then finally tackle read and write last.

1. Define contexts.

First, pick the representation of contexts. If you want to build a substitution-based evaluator (i.e., a CK machine), the contexts are just control strings. If you want to build an environment-based evaluator (i.e., a CEK machine), then the contexts must have both a control string and an environment. The lecture notes have implementations for CK and CEK machines for a trivial language to get you started.

2. Define is_value

This should be straightforward. No directions needed.

3. Define the step function.

Use the lecture notes for guidance. Since this language has several kinds of values, you should use the is_value function to make step shorter. (i.e., you don't want to specify how functions are applied repeatedly, for every type of argument.)

4. Define the driver loop.

You may simply use the loop below, though it may be instructive to insert debugging code.

let eval (e : exp) : exp =
  let rec loop ((e, cxt) : exp * context) =
    if is_value e && is_empty_context cxt then
      e
    else
      loop (step (e, cxt)) in
  loop (e, empty_context)

5. Implement read and write

These functions are meant to read and write values from standard I/O. Writing is easy to do with print_exp . To read values, you can use parse_exp , but that function accepts arbitrary expressions. But, you can use the is_value function you defined above to reject those.


Part 2 (Optional)

You must either complete this part of the assignment, or the Unification Revisited assignment.

You've implemented a CK (or CEK) machine for a simple language that supports console I/O. The next step is to run these programs on the Web. Your task is to write an OCaml program, called Server.ml with the following command-line interface:

cs691f run Server PORT FILENAME

This command must parse the program in FILENAME and launch a Web server on PORT that runs it. All I/O should occur via the Web browser instead of the console. You'll need to modify (or functorize) your CK/CEK machine to do so. You should only need to change the cases for evaluating read and write.

The Web is a very different environment from desktops. You will need to address several concerns in this assignment:

Security

You cannot trust the end-users to actually run any program. When JavaScript code is delivered to a browser, it is trivial for a user to modify it or replace it completely. In general, you cannot trust the result of any computation (i.e., JavaScript code) that runs in the browser. For this assignment, assume that all computations are critical, so do not run any code in the browser. Use the browser for I/O only.

For example, suppose you're running the following program:

      write "Enter first number";
      let x = read<int> in
      write "Enter second number";
      let y = read<int> in
      write "The sum is":
      write (x + y)
      

The addition operation at the end must be performed on the server. Therefore, the program should be transformed into three separate forms: (1) a form that displays "Enter first number" that reads in x, (2) a form that displays "Enter second number" that reads in y, and (3) a page that displays the "The sum is" and the result. These forms must be constructed on the server.

Scalability

A Web server may have to handle several concurrent sessions. Moreover, sessions typically don't terminate gracefully: users just close their browsers, which gives no notification to Web servers. For example, suppose a user is running the program above. Consider the state where the user has already entered a value for x and thus the program is now suspended waiting for y on the second form. The server could remember the x value in memory, but the user may never submit the second form. The server would either leak memory or cleanup state after some (arbitrary) timer elapses.

Instead, you should serialize the control string, send it to the client, and deserialize it to resume the computation. This will ensure that the server is truly stateless and doesn't have arbitrary timeouts to clean up state.

Semantics

Finally, users may have windows open at a site to run several computations concurrently. E.g., the user may be adding several distinct pairs of numbers in each window. These windows shouldn't interfere with each other. The easiest way to ensure you don't have any interference is to not use cookies or DOM storage at all, since they are shared between all windows at a site. Instead, store the serialized control string in a hidden form field.

Optional/Bonus: Real Security

You may have realized that a dedicated, malicious user can easily manipulate the serializing control string. Have the server either sign or encrypt the string.

Hints

  1. You'll need to parse command-line arguments. Instead of learning how to use a fancy library, I recommend just doing this:
    Array.to_list Sys.argv
    This expression returns the list of arguments as a list of strings. You can trivially pattern-match against the list to parse arguments.
  2. You'll need functions to serialize and deserialize control strings. One representation you may use is JSON:

    Handing JSON Data (Real World OCaml)

    If you want to avoid writing boring serialization code, you could use ATDgen as suggested on the page above, though I have no experience using it. Instead, you could translate the control string into an equivalent expression (a function), which can be serialized / deserialized using the provided pretty-printer / parser.
  3. You'll need to learn how to write a Web server in OCaml. I recommend using the `cohttp` and `async` libraries:
    opam install cohttp async
    The following book chapter covers these topics:

    Concurrent Programming with Async (Real World OCaml)