Code Monkey home page Code Monkey logo

typescript-multiparty-sessions's Introduction

Multiparty Session Types in TypeScript

A mono-repo for a session type API code generation toolchain for modern web programming.

This project was originally built for the author's undergraduate Master's thesis at Imperial College London.

  1. Getting started

  2. User guide

  3. Other documentation


1️⃣ Getting started

Docker workflow (recommended)

The following steps assume a Unix environment with Docker properly installed. Other platforms supported by Docker may find a similar way to import the Docker image.

$ git clone --recursive \
    https://github.com/ansonmiu0214/TypeScript-Multiparty-Sessions.git
$ cd TypeScript-Multiparty-Sessions
$ docker-compose run --service-ports dev

This command exposes the terminal of the container. To run the toolchain (e.g. show the helptext):

dev@dev:~$ codegen --help

Repository Layout

  • scribble-java contains the Scribble toolchain, for handling multiparty protocol descriptions, a dependency of our toolchain.
  • codegen contains the source code of our code generator, written in Python, which generates TypeScript code for implementing the provided multiparty protocol.
  • protocols contains various Scribble protocol descriptions, including those used in the case studies.
  • case-studies contains 3 case studies of implementing interactive web applications with our toolchain, namely Noughts and Crosses, Travel Agency, and Battleships.
  • perf-benchmarkscontains the code to generate performance benchmarks, including an iPython notebook to visualise the benchmarks collected from an experiment run.
  • scripts contains various convenient scripts to run the toolchain and build the case studies.
  • setup contains scripts to set up the Docker container.
  • web-sandbox contains configuration files for the web development, e.g. TypeScript configurations and NPM package.json files.

2️⃣ User guide

Using code generation toolchain

Refer to the helptext for detailed information:

$ codegen --help

We illustrate how to use our toolchain to generate TypeScript APIs:

Server-side endpoints

The following command reads as follows:

$ codegen ~/protocols/TravelAgency.scr TravelAgency S \
	node -o ~/case-studies/TravelAgency/src
  1. Generate APIs for role S of the TravelAgency protocol specified in ~/protocols/TravelAgency.scr;

  2. Role S is implemented as a node (server-side) endpoint;

  3. Output the generated APIs under the path ~/case-studies/TravelAgency/src

Browser-side endpoints

The following command reads as follows:

$ codegen ~/protocols/TravelAgency.scr TravelAgency A \
	browser -s S -o ~/case-studies/TravelAgency/client/src
  1. Generate APIs for role A of the TravelAgency protocol specified in ~/protocols/TravelAgency.scr;

  2. Role A is implemented as a browser endpoint, and assume role S to be the server;

  3. Output the generated APIs under the path ~/case-studies/TravelAgency/client/src

Running tests

To run the end-to-end tests:

# Run from any directory
$ run_tests

The end-to-end tests verify that

  • The toolchain correctly parses the Scribble protocol specification files, and,
  • The toolchain correctly generates TypeScript APIs, and,
  • The generated APIs can be type-checked by the TypeScript Compiler successfully.

The protocol specification files, describing the multiparty communication, are located in ~/codegen/tests/system/examples. The generated APIs are saved under ~/web-sandbox (which is a sandbox environment set up for the TypeScript Compiler) and are deleted when the test finishes.

Running case studies

Run the following to install dependencies for any pre-existing case studies:

$ setup_case-studies

We include three case studies of realistic web applications implemented using the generated APIs.

For example, to generate the APIs for the case study NoughtsAndCrosses:

# Run from any directory
$ build_noughts-and-crosses

Note that the identifier used in the build_ command converts the camelCase convention into a lower-case hyphenated string.

To run the case study NoughtsAndCrosses:

$ cd ~/case-studies/NoughtsAndCrosses
$ npm start

and visit http://localhost:8080.

Other case studies currently available include:

  • TravelAgency
  • Battleships

Running benchmarks

Run the following to install dependencies for the case studies:

$ setup_benchmarks

We include a script to run the performance benchmarks on web applications built using the generated APIs, against a baseline implementation.

To run the performance benchmarks:

$ cd ~/perf-benchmarks
$ ./run_benchmark.sh

Note: If the terminal log gets stuck at Loaded client page, open a web browser and access http://localhost:5000.

Customisation:: You can customise the number of messages exchanged and the number of runs for each experiment. These parameters are represented in the run_benchmark.sh script by the -m and -r flags respectively.

For example, to set up two configurations -- running the benchmark with 100 round trips and 1000 round trips -- and run each configuration 100 times:

$ cd ~/perf-benchmarks
$ ./run_benchmark.sh -m 100 1000 -r 100

Running ./run_benchmark.sh will clear any existing logs.

Visualising benchmarks

To visualise the performance benchmarks, run:

$ cd ~/perf-benchmarks
$ jupyter notebook --ip=0.0.0.0
/* ...snip... */
	To access the notebook, open this file in a browser:
		/* ...snip... */
	Or copy and paste one of these URLs:
	   http://dev:8888/?token=<token>
	or http://127.0.0.1:8888/?token=<token>

Use a web browser to open the URL in the terminal output beginning with http://127.0.0.1:8888. Open the Benchmark Visualisation.ipynb notebook.

Click on Kernel -> Restart & Run All from the top menu bar.

Note: If you change the message configuration (i.e. the -m flag), update the NUM_MSGS tuple located in the first cell of the notebook as shown below:

# Update these variables if you wish to
# visualise other benchmarks.
VARIANTS = ('bare', 'mpst')
NUM_MSGS = (100, 1000)

3️⃣ Other Documentation

Consult the wiki for more documentation.

typescript-multiparty-sessions's People

Contributors

ansonmiu0214 avatar dependabot[bot] avatar fangyi-zhou avatar tooni avatar

Stargazers

 avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

Forkers

tooni fangyi-zhou

typescript-multiparty-sessions's Issues

Add factory function for successor states in generated API

const logic = new Implementation.Initial({
  [Labels.S17.Destination]: async (dest) => {
    const result = await checkAvailable(dest);
    if (result.available) {
      return new Implementation.S19([
        Labels.S19.Available, [result.price], ...
      ]);
    } else {
      return new Implementation.S19([
        Labels.S19.Full, [], logic
      ]);
    }
  }
});

It should be possible to change the type definition for the async function to take one more argument before dest that offers two functions: available() and full(). These take the next logic followed by the required messages (using the vararg pattern). With that, IDE users will get better guidance towards the correct implementation. For example, if I started my function like async (nextStep, dest) => then I could write nextStep. and would get automatic completions for the choices I have.

JSON gets parsed twice if browser endpoint receives messages in certain orders

global protocol Test(role S, role C, role A) {
    login(number) from C to A;
    pay(number) from C to S;
    blah() from C to A;
    acc(number) from S to C;
}

If S sends acc(number) before C sends blah(), then you get:

"C cancelled session: JSON.parse: unexpected character at line 1 column 2 of the JSON data"

This happens because of line 300 in codegen/generator/browser/templates/runtime.tsx.j2, which should use the data variable instead of the message variable, since message has been parsed already, but then the handle function in C's receive state tries to parse it again.

I am gonna try and make a PR for this.

Receiver component doesn't re-register for messages unless state changes

I fixed this during my thesis but totally forgot about it until now. Gonna try and remember how scribble works...

global protocol ReceiverBug(role A, role B) {
  rec Loop {
    choice at A {
      msg1() from A to B;
      continue Loop;
    } or {
      msg2() from A to B;
      continue Loop;
    }
  }
}

the handler registration of B is tied to componentDidMount, but if the state didn't change — since there's just one state for B here — then that method won't get called because the receiver component never has to mount again. Without checking for this, B stops being able to receive messages after the first message

associated PR: #52

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.