🐓
Elmのデータの実行時表現を調べてみよう
ElmはJavaScriptへとコンパイルされてJavaScript環境で実行されるわけですが、実行時のデータの表現はどうなっているのかや、関数はどのような名前でマングリングされるのだろうという素朴な疑問をもったので、調べてみました。やりかたは、単純に各種のデータを変数で定義しコンパイルして実行し、DevToolsで実行時の値を覗いてみるだけです。
Elmソースコード(抜粋)
int =
42
float =
3.14
string =
"Hello, World!"
bool =
True
nothing =
Nothing
just =
Just 42
ok =
Ok 42
err =
Err "Failed"
unit =
()
tuple =
( 1, "a" )
tuple3 =
( 2, "b", False )
object =
{ familyName = "Doe", personalName = "John", phone = "090-0000-0000" }
list =
[ 0, 1, 2 ]
array_empty =
Array.empty
array =
Array.fromList [ "a", "b", "c" ]
arrays =
Array.append (Array.fromList [ "a", "b", "c" ]) (Array.fromList [ "d", "e", "f" ])
set_empty =
Set.empty
set =
Set.fromList [ 1, 2, 3, 4, 5 ]
variant_leaf =
Leaf 42
variant_branch =
Branch (Leaf 0) (Leaf 1)
dict_empty =
Dict.empty
dict =
Dict.fromList [ ( "a", 1 ), ( "b", 2 ), ( "c", 3 ) ]
type Tree a
= Leaf a
| Branch (Tree a) (Tree a)
コンパイル後のJavaScript
var variant_leaf = $author$project$Main$Leaf(42);
var variant_branch = A2(
$author$project$Main$Branch,
$author$project$Main$Leaf(0),
$author$project$Main$Leaf(1)
);
var unit = _Utils_Tuple0;
var tuple3 = _Utils_Tuple3(2, "b", false);
var tuple = _Utils_Tuple2(1, "a");
var sub_none = $elm$core$Platform$Sub$none;
var string = "Hello, World!";
var set_empty = $elm$core$Set$empty;
var set = $elm$core$Set$fromList(_List_fromArray([1, 2, 3, 4, 5]));
var ok = $elm$core$Result$Ok(42);
var object = { familyName: "Doe", personalName: "John", phone: "090-0000-0000" };
var nothing = $elm$core$Maybe$Nothing;
var list = _List_fromArray([0, 1, 2]);
var just = $elm$core$Maybe$Just(42);
var _int = 42;
var _float = 3.14;
var err = $elm$core$Result$Err("Failed");
var dict_empty = $elm$core$Dict$empty;
var dict = $elm$core$Dict$fromList(
_List_fromArray(
[
_Utils_Tuple2('a', 1),
_Utils_Tuple2('b', 2),
_Utils_Tuple2('c', 3)
]));
var bool = true;
var arrays = A2(
$elm$core$Array$append,
$elm$core$Array$fromList(_List_fromArray(["a", "b", "c"])),
$elm$core$Array$fromList(_List_fromArray(["d", "e", "f"]))
);
var array_empty = $elm$core$Array$empty;
var array = $elm$core$Array$fromList(_List_fromArray(["a", "b", "c"]));
実行時の値
array: {$: "Array_elm_builtin", a: 3, b: 5, c: Array(0), d: Array(3)}
array_empty: {$: "Array_elm_builtin", a: 0, b: 5, c: Array(0), d: Array(0)}
arrays: {$: "Array_elm_builtin", a: 6, b: 5, c: Array(0), d: Array(6)}
bool: true
dict: {$: "RBNode_elm_builtin", a: {…}, b: "b", c: 2, d: {…}, …}
dict_empty: {$: "RBEmpty_elm_builtin"}
err: {$: "Err", a: "Failed"}
just: {$: "Just", a: 42}
list: {$: "::", a: 0, b: {…}}
nothing: {$: "Nothing"}
object: {familyName: "Doe", personalName: "John", phone: "090-0000-0000"}
ok: {$: "Ok", a: 42}
set: {$: "Set_elm_builtin", a: {…}}
set_empty: {$: "Set_elm_builtin", a: {…}}
string: "Hello, World!"
tuple: {$: "#2", a: 1, b: "a"}
tuple3: {$: "#3", a: 2, b: "b", c: false}
unit: {$: "#0"}
variant_branch: {$: "Branch", a: {…}, b: {…}}
variant_leaf: {$: "Leaf", a: 42}
_float: 3.14
_int: 42
まとめ
- プリミティブな値は素直にJavaScriptのプリミティブな値に変換されています
- レコードはそのままJavaScriptのオブジェクトに変換されています
- カスタム型の値は、
{$: "Leaf", a: 42}
のようにオブジェクトに変換されています。ヴァリアント名は$
という名前のプロパティで、それからヴァリアントのフィールドは先頭からa
、b
……とアルファベット小文字の順でプロパティ名が与えられるようです - タプルは
{$: "#2", a: 1, b: "a"}
のように#2
というタグがついたオブジェクトでした。_Utils_Tuple2
みたいなユーティリティ関数で簡単に作れるようになっているようです -
Nothing
やJust
、Ok
のような組み込みのカスタム型も同様でした - ヴァリアントは
$author$project$Main$Leaf
というように$author$project$モジュール名$ヴァリアント名
という命名規則で定義された関数となっているようです -
Set
は$: "Set_elm_builtin"
なオブジェクトで、実装は赤黒木みたいです -
Dict
も赤黒木みたいです -
List
は他のカスタム型と同じように$: "::"
とかプロパティを持つオブジェクトですが、_List_fromArray
でJavaScriptの配列から簡単に作れるようです -
Array
はArray_elm_builtin
というヴァリアント名がつけられたオブジェクトになっています。a
が長さで、実際のデータはd
に生のJavaScriptの配列として格納されているようです。b
とc
はよくわかりませんでした - ちなみに各関数はカリー化されていて、呼び出すには
A3
のような関数を使います
感想
データはわりと素直なJSONオブジェクトですし、関数はカリー化されているものの素直でユニークな名前がついたただの関数で、JavaScriptからでも扱いやすそうだなあと思いました。
Discussion