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.
You can try RTT online ! (takes a few seconds to load)
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.
Here is a minimal example using RTT on a list of string:
Rtt.to_string ["Hello"; "world"];;
Evaluating this in Try RTT will give the result:
- : string = "[\"Hello\"; \"world\"]"
which is the string representing the initial value.
Here is an example program which parses an OCaml source file and displays its syntax tree:
let pprint_ast parse file =
let c = open_in file in
let ast = parse (Lexing.from_channel c) in
close_in c;
Format.printf "%t\n%!" (Rtt.pprint ast)
let _ =
Arg.parse
[]
(function file ->
if Filename.check_suffix file ".ml" then pprint_ast Parse.implementation file
else if Filename.check_suffix file ".mli" then pprint_ast Parse.interface file
else prerr_endline (file ^ " is not an OCaml source file"))
(Sys.argv.(0) ^ " <file.ml[i]>")
The output of applying it to itself looks like:
[
{
pstr_desc =
Pstr_value (
Nonrecursive,
...
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.
Installation is not difficult, but requires the OCaml sources (version 4.00.1 only), as well as an installed version of OCaml 4.00.1. Here are the steps to install:
Makefile.config to specify paths
make
sudo make install
After installing RTT you may run make test/test_rtt and
make test/test_nortt to check the installation and see a
minimal example.
The source code repository can be found on the RTT project page on the OCaml Forge.
The compilation and linking commands should look like the following:
ocamlc -pp "rtt <includes>" -I +rtt -I rtt_stubs <includes> -c <file.ml>
ocamlc <includes> -I +rtt rtt.cma <stubs> <files.cmo>
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:
-I rtt_stubs `tail -n+2 rtt_stubs/stubs.mllib | awk '{print "rtt_stubs/"$0".ml"}'`
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>.
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:
ocamlc -pp "rtt -pp \"camlp4 <options>\"" -I +rtt <file.ml>
The RTT API consists of the two following modules:
Rtt_type
defines the run-time representation of types.
Rtt_untype
provides primitive functions such as dynamic (un-)typing and generic
matching.
Rtt_ops
provides user-level rtt-related operations, such as
pprint and to_string.
Another module Rtt merges the three above modules for
convenience.
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.
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.
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.
An RTT-using program may be compiled without instrumentation, with
the following commands (which use a stub
implementation of the RTT functions):
ocamlc -I +nortt <includes> -c <file.ml>
ocamlc <includes> -I +nortt rtt.cma <files.cmo>
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:
include and module type of, for top-level modules
Besides, some other features have not been seriously tested.
This document was translated from LATEX by HEVEA.