Duneで始めるOCaml project
opam switch
opamはpackagesをglobalにインストールする
switchコマンドで隔離された環境を作ったり切り替えたりできる
Pythonのvirtualenvに近い
opam switch list
選択可能なcompilerの一覧を見る
code:shell
$ opam switch list
# switch compiler description
→ 4.10.0 ocaml-base-compiler.4.10.0 4.10.0
default ocaml-base-compiler.4.10.0 default
opam switch create
code:shell
$ opam switch create . ocaml-base-compiler.4.10.0
<><> Gathering sources ><><><><><><><><><><><><><><><><><><><><><><><><><><> 🐫
<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><> 🐫
∗ installed base-bigarray.base
∗ installed base-threads.base
∗ installed base-unix.base
∗ installed ocaml-base-compiler.4.10.0
∗ installed ocaml-config.1
∗ installed ocaml.4.10.0
Done.
local switchは~/.opamではなくそのディレクトリ内の./_opamで完結する
code:shell
$ opam switch list
# switch compiler description
→ /Users/ohbarye/.ghq/dev/playground/dune-test ocaml-base-compiler.4.10.0 /Users/ohbarye/.ghq/dev/playground/dune-test
4.10.0 ocaml-base-compiler.4.10.0 4.10.0
default ocaml-base-compiler.4.10.0 default
NOTE Current switch has been selected based on the current directory. The current global system switch is 4.10.0.
switch作成時には必要なファイルのダウンロードとcompilerのビルドが実行されるのでちょっと時間がかかる
opam install merlin ocp-indent dune utop
merlin
ocamlmerlin コマンドで利用可能で、エディタに統合できる OCaml IDE エクスペリエンスを提供するツール
コンテキスト依存の自動補完、エラー報告、型情報やドキュメントの問い合わせ、定義へのジャンプなど
エディタのプラグインで使用されるので直接使用することはほぼない
ocp-indent
OCaml のソースコードをインデントするためのシンプルでカスタマイズ可能なツール
ほとんどのエディタプラグインもこれを利用してコードを自動的にインデントする
executable
実行可能なプログラムのこと
duneの設定ファイルをS式で書く
ファイル名は dune
code:lisp
(executable
(name main))
top levelの要素を節 (stanza) という
executable節のname要素はプログラムのエントリポイントとなる
ファイルを作る
code:ocaml
let () =
print_endline "Hello, world!"
dune build main.exe
ビルド
_buildディレクトリ、.merlin dune-projectを生成する
規約なのでmacOSでもLinuxでも.exeを使う
dune clean
生成物を消し去る
dune exec ./main.exe
プログラムの実行
実際にはビルドと実行を同時に行う
root dirが散らからないようにするには、binディレクトリに移す
code:shell
$ mkdir bin
$ mv dune main.ml bin/
$ dune exec bin/main.exe
libraries
モジュールを分離したい
lib/duneを作る
code:lisp
(library
(name lib))
library節を使う
lib/math.mlを用意
code:ocaml
let add x y = x + y
let sub x y = x - y
bin/duneに依存するライブラリを指定
code:lisp
(executable
(name main)
(libraries lib))
ライブラリを利用するようにコードを変更
code:ocaml
open Lib
let () =
let result = Math.add 2 3 in
print_endline (string_of_int result);
let result = Math.sub 3 1 in
print_endline (string_of_int result)
libディレクトリがLibモジュールになり、math.mlがMathモジュールになっている
interfaces
.mliファイルを定義することで可視性をコントロールできる
APIドキュメントのコメントもインタフェースファイルに定義する
lib/math.mliを追加する
code:ocaml
val add : int -> int -> int
(** add x y returns the result of x + y. *) これでmath.mlからはaddしかexposeしないことになった
bin/main.mlがMath.subを参照しようとするとエラーになる
code:shell
$ dune exec bin/main.exe
File "bin/main.ml", line 6, characters 15-23:
6 | let result = Math.sub 3 1 in
^^^^^^^^
Error: Unbound value Math.sub
File "lib/math.ml", line 3, characters 4-7:
3 | let sub x y = x - y
^^^
Error (warning 32): unused value sub.
ライブラリをutopREPLで使う
executableとlibrariesの分離をすると良いことの1つに、REPLでライブラリの関数を試せることがある
dune utop lib
lib/の中身がbuildされ、かつloadされた状態でutopが起動する