Skip to content

QuantoCore

Aleks Kissinger edited this page Mar 14, 2013 · 3 revisions

QuantoCore is the name given to the general purpose ML library for creating and rewriting with string graphs and !-graphs. It also refers to the Controller, which is a command line tool that provides access to all of the core's functionality via a JSON protocol. While the old controller has been retained for backwards compatibility with the old Quantomatic GUI, the new controller and protocol are what we discuss here.

Notable changes from the old controller are that everything is done is JSON (no more escape-sequences), the controller functionality is implemented in modules, and calls to the core are processed and handled asynchronously.

Architecture and Protocol

The controller is a thin wrapper for a table of modules. Requests look like this:

{
 "request_id": #,
 "controller": ...,
 "module": ...,
 "function": ...,
 "input": JSON
}

For every message, the dispatch chain goes: ControllerRegistry => Controller => Module => function.

Responses look like this:

{
  "request_id": #,
  "success": true,
  "output": JSON
}

or

{
  "request_id": #,
  "success": false,
  "message": STR,
  "code": INT
}

Functions themselves are just maps from json to json. If they return normally, the first response format is used. If they raise a user_exn, the second response is used. Modules functorise over GRAPHICAL_THEORY as well as any modules they depend on.

Modules keep their own state in ref's, expose functions (typically state accessors used by themselves or other modules), and register protocol functions. This method of storing state seems to work fine, provided the functor call for each module is only called once per controller:

(* Foo and Bar share refs, but Baz's refs are distinct from Foo and Bar *)
structure Foo = Fun(X)
structure Bar = Foo
structure Baz = Fun(X)

Protocol functions: fdesc records and language bindings

Function definitions are made such that bindings can be automatically generated by ML code. Central to this is the concept of a protocol type, or ptype. A ptype is one of a short list of type symbols which the client bindings may want to handle in a particular way. Currently, ptypes are defined as:

datatype ptype =
  list_t of ptype |
  string_t | int_t | json_t |
  graphname_t | vertexname_t | edgename_t | bboxname_t | rulename_t |
  graphdesc_t | ruledesc_t

For instance, if a function takes a graphname_t as an argument, the java binding will actually take in a graph object, then call getCoreName.

New protocol functions are defined like this:

(* test named args *)
val ftab = ftab |> register
{
  name = "concat",
  doc = "Concatenates the given arguments",
  input = N ["arg1" -: string_t, "arg2" -: string_t],
  output = S string_t
} (fn x => (

let
  val s1 = arg_str x "arg1"
  val s2 = arg_str x "arg2"
in Json.String (s1 ^ s2)
end

))

It's a bit verbose, but if we try to keep modules at < 30 protocol functions a piece it should keep things pretty readable.

Register takes an fdesc followed by a function from json to json. The input and output fields can either be singletons (marked by S) or a collection of named arguments (marked by N). The benefit of this is the core now knows enough to generate documentation or code bindings on the fly. So, the last stage of the build process would be something like:

% quanto-core --java-bindings [dir]

Adding other languages (e.g. scala) would then just be a matter of implementing some ptype handlers.

Parallelisation

Clone this wiki locally