Arantium Maestum

プログラミング、囲碁、読書の話題

js_of_ocamlでゲームループを実装する方法二つ

js_of_ocamlでゲームループ的なものを実装する方法を考えてみたい。

ゲームループというのは、特定の時間デルタごとに何らかの計算と出力が行われるようなコードのことだ。今回は例によって最小構成ということで、1秒ごとにコンソールに"hello"と出力していくようにする。

いろんなやり方があると思うが、基本方針としてJavaScript的なやり方をするのか、OCaml的なやり方をするのか、の選択肢がある。

JavaScriptでやるなら一番素直なやり方はWindowオブジェクトのsetIntervalメソッドを使うことだろう。js_of_ocamlではちゃんとAPIとして提供されているのでそのままOCamlコードから使える:

open Js_of_ocaml

let hello () = print_endline "hello"

let () = ignore @@ Dom_html.window##setInterval (Js.wrap_callback hello) 1000.

setIntervalJavaScript側のメソッドなので引数もJSのオブジェクトである必要がある。JS関数と(js_of_ocamlコンパイルされる)OCaml関数では実装も型も違うため、js_of_ocamlで提供されているJs.wrap_callbackOCaml関数をJS関数に変換してやる。

OCaml風に書くなら一例としては非同期ライブラリのLwtを使って再帰でループするやり方がある:

open Js_of_ocaml_lwt

let (>>=) = Lwt.bind

let rec loop () =
  print_endline "hello";
  Lwt_js.sleep 1. >>= loop

let () = Lwt.ignore_result @@ loop ()

ちなみにこのやり方はjs_of_ocaml公式サンプルの一つを簡略化したもの:

https://github.com/ocsigen/js_of_ocaml/blob/master/examples/planet/planet.ml

この場合、js_of_ocaml-lwtを使っているのでduneファイルも:

(executable
  (name main)
  (libraries js_of_ocaml-lwt)
  (modes js)
  (preprocess (pps js_of_ocaml-ppx)))

のようにlibrariesに追加する必要がある。ちなみにjs_of_ocaml-lwtにlwt自体も含まれているようで、lwtそのものは使っているのにlibrariesに含めなくていいようだ。

さて、この二つのアプローチのどちらを選ぶか、だが。個人的にはJSの流儀でいくんだったらそもそもJS書いていればいい気もしていて、UI部分でどうしてもJSのAPIを使わなくてはいけない部分はできるだけ分離しておいて、他の部分は非同期も含めてOCaml的にやっていきたい。