]> Interfacing CDuce with OCaml

This page describes the CDuce/OCaml interface. This interface allows the programmer to:

The intended usages for the interface are:

To see how to build CDuce with support for the OCaml interface, see the INSTALL file from the CDuce distribution.

The heart of the interface is a mapping from OCaml types to CDuce types. An OCaml type %%t%% is translated to a CDuce type T(%%t%%), which is meant to be isomorphic to %%t%%: there is a canonical function %%t%%T(%%t%%) from OCaml values of type %%t%% to CDuce values of type T(%%t%%), and another canonical function T(%%t%%)%%t%%.

The canonical translation is summarized in the following box:

OCaml type t CDuce type T(t)
charByte = '\0;'--'\255;'
int-1073741824 -- 1073741823
stringLatin1 = [ Byte* ]
unit[] = `nil
boolBool = `true | `false
t1 * ... * tn (T(t1),(...,T(tn))...)
t -> s T(t) -> T(s)
t list [ T(t)* ]
t array [ T(t)* ]
A of t | B of s | C (`A, T(t)) | (`B, T(s)) | `C
[ `A of t | `B of s | `C ] (`A, T(t)) | (`B, T(s)) | `C
{ x : t; y : s } {| x = T(t); y = T(s) |}
t ref ref T(t)
Cduce_lib.Value.tAny
Cduce_lib.Encodings.Utf8.tString
Big_int.big_intInt

Only monomorphic types are handled by the interface. It is allowed to use polymorphic constructors as an intermediate, as long as the final type to be translated is monomorphic. Recursive types, including unguarded ones (option -rectypes of the OCaml compiler) are accepted. In the following example:

type 'a t = A of int | B of 'a t type s = int t type 'a u = A of ('a * 'a) u | B type v = int u

the type s can be translated, but the type v can't, because its infinite unfolding is not a regular type.

OCaml object types are not supported.

Note that values are copied in depth (until reaching an abstract type, a function types, etc...). In particular, translating an OCaml cyclic values to CDuce will not terminate (well, with a stack overflow!).

If an OCaml value has a type that can be translated, it is possible to use it from CDuce (see the How to compile and link section for more details).

In a CDuce module, you can write M.f to denote the result of translating the OCaml value M.f to CDuce.

If the value you want to use has a polymorphic type, you can make the translation work by explicitly instantiating its type variables with CDuce types. The syntax is M.f with { t1 ... tn } where the ti are CDuce types. The type variables are listed in the order they appear in a left-to-right reading of the OCaml type. Example:

let listmap = List.map with { Int String }

will return a function of type (Int -> String) -> ([Int*] -> [String*])

We have seen in the section above how OCaml values can be used from a CDuce module. It is also possible to use CDuce values from OCaml. To do so, you must give an OCaml interface (.mli) for the CDuce module (.cdo). The interface can define arbitrary types, and declare monomorphic values. These values must be defined in the CDuce module with a compatible type (subtype of the translation).

As an example, suppose you have this CDuce module (foo.cd):

type s = (`A,int) | `B let double (x : Latin1) : Latin1 = x @ x let dump (x : s) : Latin1 = string_of x

You can define an OCaml interface for it (foo.mli):

type t = A of int | B val double: string -> string val dump: t -> string

When the foo.cdo module is compiled, CDuce will look for the foo.cmi compiled interface (hence, you must first compile it yourself with OCaml), and generate stub code, so as to define an OCaml module Foo with the given interface. This module can then be linked together with other "regular" OCaml modules, and used from them.

Notes:

Here is the protocol to compile a single CDuce module:

You can then link the resulting OCaml module, maybe with other modules (either regular ones, or wrapping a CDuce module): ocamlfind ocamlc -o {{...}} -package cduce -linkpkg foo.cmo {{...}}. When the program is run, the CDuce bytecode file foo.cdo is looked in the current directory only, and loaded dynamically (with a checksum test).

It might be preferable to include the CDuce bytecode directly into the OCaml glue code. You can do this by giving cdo2ml the -static option: ocamlfind ocamlc -c -package cduce -pp "cdo2ml -static" -impl foo.cdo. Modules which have been compiled this way don't need the corresponding .cdo at runtime.

If you choose static linking, you have to use a correct ordering when linking with OCaml. Note that it is possible to mix static and dynamic linking for various CDuce modules in a same program.

Everything works mutatis mutandis with the native OCaml compiler ocamlopt.

You might need to pass extra -I flags to CDuce so that it could find the referenced .cmi files.

It is possible to run a CDuce module with cduce --run foo.cdo, but only if it doesn't use OCaml values.

Interested users can look at the output of cdo2ml to better understand how the interface works.

The tool cduce_mktop creates custom versions of the CDuce toplevel with built-in support for some OCaml modules / functions.

cduce_mktop [target] [primitive file]

The first argument is the file name of the resulting toplevel. The second points to a file whose contents specify a set of built-in OCaml values to be embedded in the toplevel. Each line must either be a qualified value (like List.map) or the name of an OCaml unit (like List). Empty lines and lines starting with a sharp character are ignored.

In a custom toplevel, the directive #builtins prints the name of embedded OCaml values.

let home = Sys.getenv "home";;

This example demonstrates how to use OCamlSDL library.

Sdl.init `None [ `EVERYTHING ];; let cd = Sdlcdrom.cd_open 0;; Sdlcdrom.cd_eject cd;;

If you put these lines in a file cdsdl.cd, you can compile and link it with:

cduce --compile cdsdl.cd -I `ocamlfind query ocamlsdl` ocamlfind ocamlc -o cdsdl -pp "cdo2ml -static" -impl cdsdl.cdo \ -package cduce,ocamlsdl -linkpkg

This example demonstrates how to use ocaml-mysql library.

let db = Mysql.connect Mysql.defaults;; match Mysql.list_dbs db `None [] with | (`Some,l) -> print [ 'Databases: ' !(string_of l) '\n' ] | `None -> [];; print [ 'Client info: ' !(Mysql.client_info []) '\n' 'Host info: ' !(Mysql.host_info db) '\n' 'Server info: ' !(Mysql.server_info db) '\n' 'Proto info: ' !(string_of (Mysql.proto_info db)) '\n' ];;

If you put these lines in a file cdmysql.cd, you can compile and link it with:

cduce --compile cdmysql.cd -I `ocamlfind query mysql` ocamlfind ocamlc -o cdmysql -pp "cdo2ml -static" -impl cdmysql.cdo \ -package cduce,mysql -linkpkg

This example demonstrates how to dynamically compile and evaluate CDuce programs contained in a string.

pr [ !id ' = ' !(string_of v) '\n' ] | (`None, f & (Int -> Int)) -> pr [ !(string_of (f 100)) '\n' ] | (`None,v) -> pr [ !(string_of v) '\n' ] with (exn & Latin1) -> print [ 'Exception: ' !exn '\n' ] ]]>

If you put these lines in a file eval.cd, you can compile and link it with:

cduce --compile eval.cd -I `ocamlfind query cduce` ocamlfind ocamlc -o eval -pp "cdo2ml -static" -impl eval.cdo \ -package cduce -linkpkg
(* File cdnum.mli: *) val fact: Big_int.big_int -> Big_int.big_int (* File cdnum.cd: *) let aux ((Int,Int) -> Int) | (x, 0 | 1) -> x | (x, n) -> aux (x * n, n - 1) let fact (x : Int) : Int = aux (Big_int.unit_big_int, x) (* Could write 1 instead of Big_int.unit_big_int. Just for fun. *)