Skip to content

js_of_ocaml勉強メモ  JavaScript側オブジェクトの操作とイベントリスナーの登録

さて、前回はOCamlをJavaScriptに変換するところまでやってみた。
今回はJavaScript側のオブジェクトをOcamlから操作するところをやってみよう。
今回も参考にしたのは「ウェブブラウザで関数型プログラミング! js_of_ocaml

JavaScript側のオブジェクトを呼び出す手順としては、まずOCaml側にインターフェースを定義して、それからOCaml側にJavaScriptのオブジェクトをletで持って来て、持って来たオブジェクトに##つけてメソッド呼び出しすればいいっぽい。

open Js

class type _window = object
  method alert : js_string t -> unit meth
  method addEventListener : js_string t -> (unit -> unit) -> unit meth
end
class type _doc = object
  method writeln : js_string t -> unit meth
end

let square (x) = x*x
let window : _window t = Unsafe.variable "window"
let doc : _doc t = Unsafe.variable "document"

let onloadcallback ( ) =
	doc##writeln ( Js.string "hello" )

let _ =
	window##addEventListener ( Js.string "mousedown" , onloadcallback)
	

これでwindowオブジェクトのaddEventListenerや、document(doc)オブジェクトのwritelnをOCaml側から動かせるようになった。

まあ今回は面倒なのでwindowのmousedownにコールバックを設定することにして、クリックしたらdocument.writelnでhelloと書かれることにした。

今回のポイントは

 method addEventListener : js_string t -> (unit -> unit) -> unit meth

の部分で、addEventListenerはJavaScriptでは、第一引数に文字列(string)、第二引数にコールバック関数を取る。だから例えばこれをJavaScript風に宣言するとすれば

addEventListener: function (string, callback)  { ... }

になるんだけど、OCamlではメソッドのインターフェースはカリー化された表現で行う。

すなわち、常に一つの引数を取る関数が連続して最後の返り値を返す、という形になる。そのため、js_string t -> (unit -> unit) -> unit となっているわけだ。

OCamlはJavaScriptと違って静的型付け言語のため、変数の型は厳格にしなければならない。
そのため、第一引数は文字列(js_string t)であり、第二引数はコールバック(unit->unit)であり、返り値はどうでもいい(unit)と宣言するわけなのだ。

OCamlのunit型はCでいうvoidだという説明が良くされるのだけど、実際には値を一つしか持たない型なのでなんでも入れることが出来るvoidとはちょっとニュアンスが違うような気がする。とはいえ、この場合は値を返さない関数であることをunitを返すことで表現しているという感じかな。

第二引数のコールバック関数の表現も、(unit -> unit)、つまり引数をとらずに値を返さない関数ということになる。

これ、イベントリスナーの場合は引数とる場合もあるんだけどその場合はどうするのかしら。まあJavaScriptとのインターフェースだからなあ。

オプション引数で定義するってのもなんかダサい。いいんだけど。

OCamlはprintfの実装もけっこう無理がある仕様らしいので(参照)、このへんはあまり深くは考えない感じにしようかしら。うーん、でも気になる。

どうすんだ、と思ったら良く考えるとjs_of_ocamlにはもともとDomとか色々便利系のライブラリが揃っていて、WebGLまで使えると言う豪華なやつだったことを思い出した。

そこでそれを使って書き直すと、こうなる

module Html = Dom_html
let h = 20.
let w = floor (h *. sqrt 3. /. 2. +. 0.5)
let n = 12

let create_canvas () =
  let d = Html.window##document in
  let c = Html.createCanvas d in
  c##width <- 320;
  c##height <- 320;
  c

let start _ =
(* Html.window##alert ( Js.string "hofge" );*)
  let c = create_canvas () in
  c##onclick <- Html.handler 
    (fun ev ->
       let mx=ev##clientX and my=ev##clientY in
        Html.window##alert ( Js.string ( "x:" ^ (string_of_int mx) ^ "y:" ^ (string_of_int my) ));
       Js._true);
  Dom.appendChild Html.window##document##body c;
  let c = c##getContext (Html._2d_) in
  c##globalCompositeOperation <- Js.string "copy";
  c##fillStyle <- Js.string "#ccffd9";
  c##beginPath ();
  c##moveTo (50. , 50.);
  c##lineTo (200., 50.);
  c##lineTo (200., 200.);
  c##lineTo (50., 200.);
  c##fill ();
  Js._false

let _ =
	Html.window##onload <- Html.handler start

おおなんだ。すげースッキリ書ける。
ということはenchant.js用のラッパーを書けばOCamlでスッキリとenchant.jsのゲームが作れるかもしれないなあ

このエントリーをはてなブックマークに追加
はてなブックマーク - js_of_ocaml勉強メモ  JavaScript側オブジェクトの操作とイベントリスナーの登録
Post to Google Buzz
Share on GREE

Related posts:

  1. JavaScriptでMMOGをつくってみよう その7 JSON-RPC風通信
  2. enchant.jsのクラス継承とJavaScriptのクラス継承のちょっとした違い
  3. 日本語スクリプト言語JapaScript
  4. JavaScriptが27%も高速化できる!?新言語JSXが登場!
  5. お絵描きアプリで学ぶHTML5のCanvas超速入門 #1.5 JavaScriptで描いた絵をサーバを使わずにセーブする!?

Facebook comments:

Post a Comment

Your email is never published nor shared. Required fields are marked *
*
*