js_of_ocaml

ハマったところ幾つかメモ。Js.Unsafeは名前の通りUnsafeだった。

Js.Unsafe.variable

Js.Unsafe.variableはごく普通にjs_of_ocamlが生成する関数内のスコープでが展開される。
何が起こるかというと、js_of_ocamlが生成する変数と名前の衝突が起こり、本当に見たかった変数が隠されてしまう。

let index_of array value from_index =
  Unsafe.meth_call
    (Unsafe.variable "$") "inArray"
    [|Unsafe.inject value;
      Unsafe.inject array;
      Unsafe.inject from_index|];;

で、$が隠されて意図した動作をしなかった。
厄介なことに -pretty の有無で変数名が変わるのでしばらく発覚していなかった。

let index_of array value from_index =
  Unsafe.meth_call
    (Unsafe.variable "jQuery") "inArray"

に変更して対処。
別名がない3文字程度までの外部の変数を参照する必要がある場合は、一端長い名前の別名をJavaScript側で定義するのが安全だと思う。

Js.Unsafe.fun_call

JavaScriptの関数呼び出しで

function js_foo(a) {
  return doA(a);
}
let foo: a -> b =
  Unsafe.variable "js_foo"

ignore (foo bar);;

としていた。

これがライブラリのバージョンアップで

function js_foo(a, b) {
  if (typeof a === "undefined") {
    return doA(a);
  } else {
    return doAB(a, b);
  }
}

となっていた。これはJavaScriptでは合法。
だけどOCaml側では foo a; が js_foo への部分適用の状態になるので、b -> cが帰る。
さらに戻り値がメソッドチェーン用で使わないのでignoreしてたのも事態をややこしくしてた。

ここは面倒くさがらずに Unsafe.fun_call を使うようにする。

let foo a =
  Unsafe.fun_call (Unsafe.variable "foo") [| Unsafe.inject a |];;