RTT: Run-time types for OCaml

RTT is a prototype implementation of run-time typing in OCaml. It
allows programmers to use functions such as to_string: α
→ string (for all α). The present solution
focuses on being fully automatic, and providing the actual run-time
type of values (including when it is more precise than the static
type, because of polymorphism). On the other side, types are
represented in an untyped way (no GADTs) and we only provide a minimal
library for the purpose of the demonstration.

RTT is implemented as a pre-processor which is inserted in the
compilation command with the usual -pp option. Programs
compiled with run-time types may use the RTT API to benefit from the
added functionality.

This example illustrates two things. First, Rtt.pprint really
uses the dynamic type of ast (which can be either
Parsetree.structure or Parsetree.signature). Second, a
run-time representation is available even for types defined in
external, normally compiled libraries, as long as they are not
abstract.

In the above commands, <includes> is the set of
include (-I) options, which must be passed to RTT (since it
is a transformation of typed programs), and
<stubs> is the list of stubs generated by
RTT for the external libraries used by the program (other than the standard library). The quickest solution is to replace <stubs> with:

which will insert all .ml stub files so that they are
compiled and linked at the same time. A more scalable option is to
compile separately all .ml files in the sub-directory
rtt_stubs created by RTT (.mli files are compiled
automatically), then create a library stubs.cma according
the order specified by the file rtt_stubs/stubs.mllib, and
use this library in place of <stubs>.

Using RTT with a pre-processor

Currently, combining RTT transformation with other (syntactic)
pre-processing (typically camlp4) should be done by passing the
pre-processing command to rtt itself, as follows:

Rtt_ops
provides user-level rtt-related operations, such as
pprint and to_string.

Another module Rtt merges the three above modules for
convenience.

RTT-compiled OCaml standard library

Calling RTT with option -rtt-stdlib, and linking the
resulting program with the file rtt_stdlib.cma (in addition to
rtt.cma) will replace the OCaml standard library by an
RTT-compiled version, which will provide maximum
accuracy. Unfortunately, the types in the RTT standard library are not
compatible with the original standard library, so in practice this
feature can only be used if all the libraries used by a program have
been compiled with rtt -rtt-stdlib.

Other options

The options -nostdlib and -nopervasives of rtt
should be used consistently with the enclosing compilation command.
The option -text causes RTT to output a pretty-printed source
file instead of an OCaml AST. This is mainly useful for debugging.

Other modules used internally

Some other modules are visible but not meant to be used directly:

Rtt_type.Rtt_predef contains the representation of
primitive types. It is opened implicitly at the beginning of
pre-processed source files.

Rtt_stdlib is the packed rtt-compiled OCaml standard
library which is implicitly opened by the -rtt-stdlib option
of RTT. It is not advised to use it independently, unless using a
library compiled with rtt -rtt-stdlib in an untransformed
program. In this case, the non-preprocessed source files may need to
open Rtt_stdlib explicitly.

A customized toplevel is provided: rttocaml, which already
contains the RTT library and the RTT-compiled OCaml standard
library. To use it, just launch rttocaml and start using
functions from module Rtt. The resulting types printed when
evaluating a phrase are those of the transformed program, which may
help to understand the transformation.

RTT handles a comprehensive subset of OCaml which allows for real
experiments, but it is still an ongoing work.

Currently, the most annoying limitation concerns the preservation of
type and module equalities when constraining a module to a signature,
if this module is (or contains) a functor. The problems comes from the
fact that when casting a module to a module type refines the type of
some values, these need to be redefined by the RTT transformation
because their added rttype parameters change (both their number
and their use in the function’s body). Applying a functor to a module
is an implicit case of casting, and this implies that the types
contained in the application result no longer have the path
F(M).t, but something like F(wrap M).t, which are not
considered equal by OCaml’s notion of applicative functors. The same
problem occurs when casting the functor itself to a functor type the
arguement of which contains values with a more general type. In all
these cases, the transformed program may not type-check if typing the
original program relies on functors being applicative.

Other than that, the following language features are not supported:

objects, classes, and class types

GADTs

first-order modules

recursive modules, if there are mutually recursive type
definitions split accross modules: the representation generated for
them is a recursive definition of values split accross modules,
which is rejected by OCaml

include and module type of, for top-level modules

locally abstract types are stubbed (due to a missing
information in typedtrees)