OCaml ちょっとやってみるための準備(Docker + Ubuntu 18.04)

  • Docker + Ubuntu 18.04 での環境の用意
  • とりあえず cat コマンドを書いてみた

たぶん3日坊主になると思うので、 ホスト側の環境を汚さないように Docker で環境を用意する。 ホストも Ubuntu です。

この記事を参考に。何も分からないので助かります。
WSL の Ubuntu 18.04 で OCaml 開発環境を作る - Qiita

FROM ubuntu:18.04

RUN apt update \
  && apt install -y --no-install-recommends \
    ca-certificates \
    curl \
    m4 \
    nano \
    ocaml \
    opam \
  && apt-get clean \
  && rm -rf /var/lib/apt/lists/*

ARG USER
ARG GROUP

RUN groupadd ${USER} \
  && useradd ${USER} -g ${GROUP} -m

USER ${USER}

RUN mkdir /home/${USER}/work

WORKDIR /home/${USER}/work

コーディングはホスト側で行うので Emacs は省く。

イメージをビルド

docker build \
  --build-arg USER=$USER \
  --build-arg GROUP=$(id -gn) \
  -t ocaml-env:test .

コンテナを起動

docker run -it \
  -v ${PWD}:/home/${USER}/work \
  --name ocaml-env-container \
  ocaml-env:test

コンテナ内で残りの作業。 コーディングはホスト側で行うので tuareg などは省いて utop だけインストール。

opam init -y

eval `opam config env`

echo "" >> ~/.bashrc
echo 'eval `opam config env`' >> ~/.bashrc

opam install -y utop

yes | opam install ... だとうまくいかなくて仕方なく対話的にやっていたけど、 -y オプションを見つけて解決した。 ので、これらの opam まわりの作業も Dockerfile に入れてしまえるはず。)

exit で抜けたりした後またコンテナ内に入りたいときは

# コンテナが止まっていたら start で再開して
# docker start ocaml-env-container

docker exec -it ocaml-env-container bash

ホスト側でコードを書く。

https://github.com/ocaml/tuareg

から tuareg.el だけ取ってきてカレントディレクトリに置いて、 Emacs

(progn
  (load (concat (file-name-as-directory default-directory)
                "tuareg.el"))
  (tuareg-mode))

を評価すると、構文ハイライトされて M-;コメントアウト・コメントインできるようになる。 賢い補完とかはできないけど、そこらへんは3日坊主にならなかったら整えるということで。

(2020-02-27 追記: tuareg-opam.el も必要)


(2020-12-29 追記)

tuareg-opam.el もカレントディレクトリに置いた上で、カレントディレクトリを load-path に追加して下記のようにしました。 tuareg.el 内で tuareg-opam を require しているようです。

(progn
  (add-to-list 'load-path default-directory)
  (load "tuareg.el")
  (tuareg-mode))

cat コマンド書く。

参考: お気楽 OCaml プログラミング入門 (8) ファイル入出力 | M.Hiroi's Home Page

参考にして書いてたつもりが、いじってたら結局 M.Hiroi さんのコード例のまんまになってしまいました。

(* cat.ml *)

let () =
  let rec cat_line () =
    print_string (read_line ());
    print_newline ();
    cat_line () in
  try
    cat_line ()
  with
    End_of_file -> ();;

コンテナ内で実行。

ls | ocaml cat.ml

2021-06-10 追記

1バイトずつ読み書きするバージョンの cat:

let () =
  let rec cat_byte_sub () =
    output_byte stdout (input_byte stdin);
    cat_byte_sub ()
  in
  try cat_byte_sub () with End_of_file -> ()

さらに \n<LF>\n に変換するようにしたもの:

let () =
  let print_byte (byte : int) =
    match byte with
    | 10 -> print_string "<LF>\n"
    | _  -> output_byte stdout byte
  in
  let rec cat_byte_sub () =
    print_byte (input_byte stdin);
    cat_byte_sub ()
  in
  try cat_byte_sub () with End_of_file -> ()

2021-06-17 追記

https://hub.docker.com/r/ocaml/ocaml

試していませんが Docker Hub にそれっぽいイメージがありました。 official image の表記がない点に注意。

関連

memo88.hatenablog.com

このときは(サードパーティのライブラリを使わないため)opam は不要で、ocamlc/ocamlopt コマンドだけ使えればいいやということになり、ocaml パッケージのみインストールして使いました。 リポジトリに Dockerfile があるのでそれを見ていただければと。