このページでは、CDuce/OCamlインタフェースについて説明する。このインタフェースによって、次のことができるようになる。
インタフェースの意図される使用法は、次のものがある。
CDuceをOCamlインタフェースのサポート付きでビルドする方法を知るには、CDuceの配布物のINSTALLファイルを参照すること。
インターフェースの中心は、OCamlの型からCDuceの型へのマップである。OCamlの型tは、tの同形であることを意味するCDuceの型T(t)に翻訳される。OCamlの型tの値からCDuceのT(t)型の値に変換する標準関数t → T(t)があり、また別の標準関数T(t) → t
char、int、string、unitは、Byte = '\0;'--'\255;'、-1073741824 -- 1073741823、Latin1 = [Byte*]、[] = `nilにそれぞれ翻訳される。t1 * ... tnは、ネストされたCDuceの直積型(T(t1),(...,T(tn))...)に翻訳される。関数型t -> sは、T(t) -> T(s)に翻訳される。矢印の引数にあるラベルは、捨てられる。t listは、同形のシーケンス型[ T(t)* ]に翻訳される。配列型t arrayは同様の翻訳になる。t optionは、型[ T(t)? ]に翻訳される。A1 of t1 | ... | An of tnのバリアント型は、型(`A1,T(t1)) | ... (`An,T(tn))に翻訳される。コンストラクタAiが引数を持っていなければ、結果の項は(`Ai,[])でなく`Aiとなる。多相バリアント型も同様に翻訳される。{ l1 : t1; ...; ln : tn }のレコード型は、閉じたレコード型{ l1 = T(t1); ... ; ln = T(tn) }に翻訳される。可変のフィールドは、単に複製される。t refはCDuceの参照型ref T(t)に翻訳される。Camlの参照をCDuceに変換した場合、結果の参照に対する操作(set、get)はもとの参照を参照する。しかしながら、CDuceの参照をOCamlに変換した場合、参照の内容を取ってきて(get)、OCamlの参照が生成される(複製という意味である)。Cduce_lib.Value.tは、CDuceの型Anyに翻訳される。これは、CDuce及びOCamlの間の間で複雑な値を翻訳したり、逆翻訳したりする際に、複数回の複製を避けるために使われる。型Cduce_lib.Encodings.Utf8.tはCDuce型Stringに翻訳される。型Big_int.big_intはCDuceの型Intに翻訳される。tはCDuceの型!tに翻訳される。この型は、単に抽象型の値のコンテナとして振舞う。CDuceはこの型の値を生成せず、この値の内容を調べることはできない(型検査は除く)。標準の翻訳を、次の表にまとめた。
OCamlの型t | Duceの型T(t) |
|---|---|
char | Byte = '\0;'--'\255;' |
int | -1073741824 -- 1073741823 |
string | Latin1 = [ Byte* ] |
unit | [] = `nil |
bool | Bool = `true | `false |
t1 * ... * tn | (T(t1),(...,T(tn))...) |
t -> s | T(t) -> T(s) |
t list | [ T(t)* ] |
t array | [ T(t)* ] |
t option | [ 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.t | Any |
Cduce_lib.Encodings.Utf8.t | String |
Big_int.big_int | Int |
このインタフェースは単相型だけを扱う。多相コンストラクタは、最終的な型が単相に翻訳される限りにおいて、中間物として使うことができる。再帰型はガードされていないもの(OCamlコンパイラのオプション-rectypes)を含め、受理される。例、
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
において、型sは翻訳できるが、型vはできない。無限の展開が通常の型にならないためである。
OCamlのオブジェクト型はサポートされていない。
値は徹底的に(抽象型、関数型などに達するまで)複製されることに注意。特に、OCamlの循環値をCDuceに翻訳すると、終了しない(スタックオーバーフローになる)。
OCamlの値が翻訳できる型を持っていれば、CDuceから使用することができる(詳しくは、コンパイルとリンクの項を参照)。
CDuceのモジュールにおいて、M.fを書いて、OCamlの値M.fをCDuceに翻訳した結果を意味することができる。
使いたい値が多相型であれば、CDuce型の型変数を明示的にインスタンス化することで変換が動作するようになる。構文は、tiをCDuceの型としてM.f with { t1 ... tn }である。型変数はOCaml型を左から右へ読んだときに現れる順序で列挙する。例:
let listmap = List.map with { Int String }
は、関数型(Int -> String) -> ([Int*] -> [String*])を返す
上の項でOCamlの値をCDuceのモジュールから使うことができることを見てきた。CDuceの値をOCamlから使うこともできる。そうするためには、CDuceのモジュール(.cdo)に対するOCamlのインタフェース(.mli)を与えなければならない。インタフェースは任意の型と単相型の値を定義できる。これらの値はCDuceのモジュール中で互換性のある型(翻訳の部分型)で定義されていなければならない。
例えば、次のようなCDuceのモジュールを考える(foo.cd)。
type s = (`A,int) | `B let double (x : Latin1) : Latin1 = x @ x let dump (x : s) : Latin1 = string_of x
これに対して、次のようなOCamlのインタフェースで定義することができる(foo.mli)。
type t = A of int | B val double: string -> string val dump: t -> string
foo.cdoモジュールがコンパイルされていれば、CDuceはfoo.cmiコンパイル済みインタフェースを探し(よって、最初に自分でOCamlでコンパイルしておかなければならない)、与えられたOCamlのモジュールFooを定義するスタブコードを生成する。そうすると、このモジュールは、ほかの"普通の"OCamlのモジュールと一つにリンクすることができるようになる。
注意:
ここで、CDuceのモジュール一つをコンパイルする方法を示す。
ocamlc -c foo.mliでOCamlのファイルから.cmiを生成する。cduce --compile foo.cdとCDuceのファイルをコンパイルする。このコマンドはCDuceのバイトコードファイルfoo.cdoを生成する。このファイルは、、CDuceの値をOCamlの値としてエクスポートしたり、OCamlの値をCDuceモジュールの中で使ったりするための、OCamlのグルーコードも含んでいる。ocamlfind ocamlc -c -package cduce --pp "cduce --mlstub" -impl foo.cdoとOCamlのグルーコードをコンパイルする。--mlstubオプションはCDuceにOCamlのグルーコードを抽出させるように支持する。cduce --mlstubを.cdoファイルに対して直接実行させれば、OCaml/CDuceインタフェースがどう動作しているかうまく理解することができるだろう。注意: コマンドcduce --mlstubは、ユニットに対する.cmiファイルを見つけなければならない。このファイルがカレントディレクトリにないならば-Iフラグを(また他の.cmiが参照されればさらにフラグを)加える必要がある。
必要な変更を加えれば、ネイティブOCamlコンパイラocamloptでも全て動作する。
参照される.cmiファイルが見つけられるように、-Iフラグを追加で渡す必要があるかもしれない。
CDuceのモジュールをcduce --run foo.cdoで実行することができるが、OCamlの値を使っていないときのみである。もしOCamlの値を使っていれば、(コンパイル及び実行に)カスタム版のcduceを生成する必要がある。参照: OCamlをトップレベルから呼び出す。
ツールcduce_mktopは、OCamlモジュール/関数へのビルトインサポートが付いた、カスタム版のCDuceトップレベル/コンパイラ/インタプリタを生成する。
cduce_mktop [-I path | -p package | -l unit ... | -byte ] [target] [primitive file]
target引数は、結果としてできるトップレベルのファイル名である。primitive file引数は、その内容でトップレベルに埋め込むべきビルトインのOCamlの値の集合を指定しているファイルを指す。各行は(List.mapのような)修飾された値か、(Listのような)OCamlのユニットの名前かのいずれかでなければならない。空行や、シャープ文字で始まる行は、無視される。
-byteフラグは、バイトコード版のトップレベルを生成ことを強制する(デフォルトではトップレベルはocamloptを使って生成される)。
最初の-Iオプションは、OCamlのユニットに対する検索パスを追加する。-pオプションは、同様の目的のために提供されている。これらの引数亜h、findlibパッケージ名である。これらのパスはトップレベルを生成するときにインクルードされる。-lオプションは、リンクすべきOCamlのユニット(例えば、x.cmx又はx.cmxa)を与える(-pオプションは、自動的にユニットをインクルードする)。
カスタムトップレベルは、命令#builtinsで組み込みのOCamlの値の名前を印字する。
let home = Sys.getenv "home";;
この例ではOCamlSDLライブラリの使い方を実演している。
Sdl.init `None [ `EVERYTHING ];; let cd = Sdlcdrom.cd_open 0;; Sdlcdrom.cd_eject cd;;
ファイルcdsdl.cdにこのコードを入れれば、次のようにコンパイル及びリンクができる。
cduce --compile cdsdl.cd -I `ocamlfind query ocamlsdl` ocamlfind ocamlc -o cdsdl -pp "cduce --mlstub" -impl cdsdl.cdo \ -package cduce,ocamlsdl -linkpkg
この例では、ocaml-mysqlライブラリの使い方を実演している
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' ];;
ファイルcdmysql.cdにこのコードを入れれば、次のようにコンパイル及びリンクができる。
cduce --compile cdmysql.cd -I `ocamlfind query mysql` ocamlfind ocamlc -o cdmysql -pp "cduce --mlstub" -impl cdmysql.cdo \ -package cduce,mysql -linkpkg
この例では、文字列を含んだCDuceのプログラムを動的にコンパイル及び実行する方法を実演している。
let pr = Cduce_lib.Value.print_utf8
try
let l = Cduce_lib.Cduce.eval
"let fun f (x : Int) : Int = x + 1;;
let fun g (x : Int) : Int = 2 * x;;
let x = getenv ['HOME'];;
f;; g;;
let a = g (f 10);;
"
in
let _ = map l with
| ([id], v) ->
pr [ !(string_of id) ' = ' !(string_of v) '\n' ]
| ([], f & (Int -> Int)) ->
pr [ !(string_of (f 100)) '\n' ]
| ([], v) ->
pr [ !(string_of v) '\n' ]
in []
with (exn & Latin1) ->
print [ 'Exception: ' !exn '\n' ]
ファイルeval.cdにこのコードを入れれば、次のようにコンパイル及びリンクができる。
cduce --compile eval.cd -I `ocamlfind query cduce` ocamlfind ocamlc -o eval -pp "cduce --mlstub" -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. *)