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.
setInterval
はJavaScript側のメソッドなので引数もJSのオブジェクトである必要がある。JS関数と(js_of_ocamlでコンパイルされる)OCaml関数では実装も型も違うため、js_of_ocamlで提供されているJs.wrap_callback
でOCaml関数を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的にやっていきたい。