LoginSignup
22
20

More than 5 years have passed since last update.

ClojureScript、Wisp、js_of_ocaml で いつもの window.addEventListener(...) を比較してみる

Last updated at Posted at 2014-09-07

JavaScriptで何はともあれ書くコードに、window.addEventListenerがありますよね。

window.addEventListener("load", function(){
  console.log("loaded");
});

DOMが初期化されるとコールバックが発火され、DOMがJavaScriptから読み書き可能になります。

jQueryを使う場合はこう。

$(function(){
  console.log("loaded");
});

(正確にはjQueryが監視するイベントはloadではなくDOMContentLoadedですが)

関数型言語のアプローチで書き比べてみる

なぜ関数型AltJSで比較するか、それは、個人的な言語設計的な興味と、実用を志向するとどうなるのかが同時にわかっておいしい。関数型言語、副作用を意識してないのと、JSに型がないのでゆるふわな状態をどう管理するかが肝になります。

(僕のAltJSに対する興味は実用JavaScriptの為でもありますが、言語設計的な観点でもあるので)

比較対象

clojure-script

clojure/clojurescript

ClojureのAltJS。他の言語のとの大きな違いは、Clojureプロジェクトの一環として開発されており、そこそこ枯れています。

wisp

Gozala/wisp

個人的に昔から気になってたLispのAltJSで、最近使ってみたらかわいかった。

ClojureScriptと比べてコンパイル速度が高速なのと、JSとほぼ一対一に変換される可読性の高さがウリです。マクロも使えます。

wispifyというbrowserifyのtransformもあるので、こっそりプロジェクトに混ぜて使えますね!

js_of_ocaml

ocsigen/js_of_ocaml

ocaml to js。ocaml界では結構使われているそうです。僕は全然使いこなせてませんが。
ocamlのバイナリ一歩手前の中間表現からJavaScriptに落としているそうです。

書き比べ!

JavaScript

var onload = function(){
  console.log("loaded");
};

window.addEventListener("load", onload);

関数定義部分と、呼び出し部分をわけて見たいので、この定義をなぞることにします。

ClojureScript

(defn onload []
  (.log js/console "loaded"))
(.addEventListener js/window "load" onload)

js/の名前空間はほぼwindowに対応していると思って大丈夫です。

Wisp

(defn onload []
  (console.log "loaded"))
(window.addEventListener "load" onload)

js名前空間がなく、シンボルとして . を含んだものを指定可能です。これは変換後のJavaScriptと対応している点でも、非常にわかりやすい。S式最高ですね。

js_of_ocaml

公式の例をいじってみるととこんな感じでした。

let onload ev =
  Js.Unsafe.eval_string "
    console.log(\"loaded\");
  ";
  Js._true

let () = ignore begin
  Dom_html.addEventListener
    Dom_html.window
    Dom_html.Event.load
    (Dom_html.handler onload)
    Js._true
end

組み込み用にラップされたものばっかりで、実体がよく見えないですね。無理やりUnsafeなオブジェクトで呼んでみましょう。

let () =
  ignore (Js.Unsafe.variable "window")##addEventListener((Js.string "load"), (Js.wrap_callback onload))

型が厳しい世界で無理に関数を呼び出してるのでignoreとかUnsafeが飛び交う、非常に危険そうにみえるコードです。まぁ、型が厳しい言語からみたら生のJSはこれぐらい危ないことをしているんだよって示唆でしょうが。

JavaScriptオブジェクトへの関数コールは、引数一つのタプルであることに注意してください。(これで今日三時間ぐらいハマってました… 普通に引数として渡そうとすると、部分適用しようとして落ちます)

比較表

javascript clojure-script wisp js_of_ocaml
グローバル名前空間 window js/window window Dom_html.window
関数呼び出し console.log(...) (.log console ...) (console.log ...) Dom_html.window##console##log((...))
メンバーアクセス obj.name (.-name obj) obj.name obj##name
メンバー書き換え obj.name = 'value' (set! (.-name obj) "value") (set! obj.name "value") obj##name <- "value"

JavaScript側は言語仕様がそこまで複雑なわけではないので、これだけ知ってればあとはその言語の世界で完結できるんじゃないでしょうか。

僕はJS固有ドメインよりその言語のドメインで表現するのに苦労することが多いです。

22
20
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
20