---
breaks: false
tags: public-tech
---
# OCaml でコードを書き始めるチュートリアル
この記事は [OCaml Tips Advent Calendar 2022](https://adventar.org/calendars/8396) の一日目です。
OCaml でコードを書くときは [OPAM](https://opam.ocaml.org/) をまず入れておきます。
```
$ bash -c "sh <(curl -fsSL https://raw.githubusercontent.com/ocaml/opam/master/shell/install.sh)"
```
でもって、OCaml を入れます。グローバルに入れてもいいですが(`npm -g` 相当)、
プロジェクトごとに OCaml のバージョンやライブラリのバージョンを変えたくなるので、
プロジェクトごとにローカルにインストールすることもできます。ここではそれでいきます。
記事執筆時点で最新の OCaml v4.14.0 を入れます。
```
$ cd /path/to/project
$ opam switch create . 4.14.0
```
でもってビルドシステムの `dune` を入れます。
```
$ opam install dune
```
新しいプロジェクトを始めるためのスケルトンを作ります。
`yourfavname` には好きな名前を入れます。
```
$ dune init project yourfavname
$ cd yourfavname
$ tree .
.
├── bin
│ ├── dune
│ └── main.ml
├── dune-project
├── lib
│ └── dune
├── test
│ ├── dune
│ └── yourfavname.ml
└── yourfavname.opam
3 directories, 7 files
```
ビルドして実行するには `dune exec` を使います。
ビルドだけなら `dune build` です。テストは `dune runtest` です。
```
# ビルド
$ dune build bin/main.exe
# ビルド+実行
$ dune exec bin/main.exe
# ビルド+テスト
$ dune runtest
```
スケルトンの中身を見てみます。本体は `bin/main.ml` にあります。
```ocaml=
$ cat bin/main.ml
let () = print_endline "Hello, World!"
```
この構成では `lib/` 配下にメインのコードをおいて、それを `bin/` 配下と
`test/` 配下で使うことを想定しています(多分)。ということで、
`lib` 配下で定義した関数を `bin` で呼び出してみます。
ここで注意すべきなのは、`bin/` の下にあるコードから見えるのは `lib/` にある
`Yourfavname` モジュールだけということです[^only-yourfavname-module]。
そこで、まず `lib/hello.ml` に
実行したい関数を定義して `Hello` モジュールを作り、この `Hello` モジュールを
`Yourfavname` モジュール内に配置することで、`Yourfavname` モジュール経由で
`Hello` モジュールにアクセスできるようにします。
[^only-yourfavname-module]: これに気づくまでに随分時間がかかりました。
```ocaml=
$ vim lib/hello.ml
let hello () = "Hello, World!"
$ vim lib/yourfavname.ml
module Hello = Hello
$ vim bin/main.ml
open Yourfavname
let () = print_endline (Hello.hello ())
$ dune exec bin/main.exe
```
次に `Hello.hello` 関数のテストを書いてみます。テストフレームワークとして
[Alcotest](https://github.com/mirage/alcotest)を使うことにします。
```
$ opam install alcotest
```
`test/dune` を編集します:
- `libraries` stanza で Alcotest を使うこと、及び `lib/` 配下の `Yourfavname` モジュールに依存することを明示します。
- `tests` stanza と `names` stanza を使って、複数のテストをスペース区切りで記述できるようにしておきます。
- `test_hello` という名前のテストを追加します。
```
$ vim test/dune
(tests
(names test_hello)
(libraries yourfavname alcotest))
```
テストを書きます。
```ocaml=
$ vim test/test_hello.ml
open Yourfavname
let test_hello () =
assert (Hello.hello () = "Hello, World!");
()
let () =
let open Alcotest in
run "Hello" [ ("hello-function", [ test_case "unit" `Quick test_hello ]) ]
```
テストを動かします。
```
$ dune runtest
Testing `Hello'.
This run has ID `DYPGZF4H'.
[OK] hello-function 0 unit.
Full test results in `/gunjo/anqou/pievyt/test_ocaml/yourfavname/_build/default/test/_build/_tests/Hello'.
Test Successful in 0.000s. 1 test run.
```
一度通ったテストは、その後コードの変更があるまで動かなくなります。強制的に
動かしたいときは `-f` オプションを使います。
```
$ dune runtest
# 何も表示されない
$ dune runtest -f
Testing `Hello'.
This run has ID `0ECRMWON'.
[OK] hello-function 0 unit.
Full test results in `/gunjo/anqou/pievyt/test_ocaml/yourfavname/_build/default/test/_build/_tests/Hello'.
Test Successful in 0.000s. 1 test run.
```
以下続く記事は、全て上の手順で作ったディレクトリ構造を前提にしています。
## 参考
- https://gitlab.com/dailambda/plebeia
- https://dune.readthedocs.io/en/stable/quick-start.html
- https://camlspotter.gitlab.io/blog/2018-08-08-opam-switch/