Posted at
ElmDay 5

Elm の最適化オプション --optimize

Elm 0.19 で新しいオプション --optimize導入されました。

記事を見ただけだと実際に何をしているのかがちょっとわかりにくいので説明します。


関数単位のデッドコード除去(--optimize 不要)

まずデッドコード除去ですが、実は --optimize フラグをつけなくてもデフォルトで有効です。

このデッドコード除去は記事に解説がある通り関数単位で行われます。つまり、大量の関数を用意しているモジュールの一部だけを利用する場合にも、使った関数だけがアウトプットに含まれます。


レコードのフィールド名の変更

こちらは --optimize の機能です。

コード上でどんなに長いフィールド名 student.mostRecentGrade でも student.m のような短い変数名に変更します。

JavaScript で同じことをやろうとしても、 student['mostRecent' + info] のように動的なフィールド名を使ってしまうのでどうしても難しいです。Google Closure Compiler の ADVANCED なんとかオプションで同じことができますが、そのためには慎重に規約を守る必要があって大変です。それに npm で大量にインストールしたパッケージはそんなことを守っていないので適用できません。

レコード名以外には、カスタム型のコンストラクタ名も短縮されます。それから、次のようにコンストラクタが一つだけの場合は、そもそもそれがなかったことにします。要するに実行時に wrap / unwrap が発生しなくなります。

type MyType = MyConstructor String


制約: Debug モジュールの禁止

--optimize を使うための条件は「Debug モジュールを一切使わないこと」です。

なぜこんな制約があるのでしょうか?

たとえば Debug.toString はデバッグのためにレコードやコンストラクタの名前を文字列に変換します。

ところがこの文字列が --optimize によって変わってしまうと、本番のみ不慮のトラブルに遭遇しかねませんからね。

ところで、Elm のパッケージは --optimize が効く状態でないと publish できません。つまり、インストールしたパッケージは全て --optimize が効くということです!


さらに最小化する

実は --optimize が面倒を見てくれるのは Elm 特有の最適化(=レコードのフィールド名などの短縮)だけで、 minify まではしてくれません。

出力された JavaScript のサイズをさらに最小化するには、 ugulifyjs の手を借りましょう。(ugulifyjs を使っていても、 --optimize を通した後の方が最終的なファイルサイズは小さくなります。)

その方法はここで紹介されています。

https://elm-lang.org/0.19.0/optimize

ざっくりこんな感じです。

$ elm make src/Main.elm --optimize --output=elm.js

$ uglifyjs elm.js --compress 'pure_funcs="F2,F3,F4,F5,F6,F7,F8,F9,A2,A3,A4,A5,A6,A7,A8,A9",pure_getters,keep_fargs=false,unsafe_comps,unsafe' | uglifyjs --mangle --output=elm.min.js

こんなの毎回書いてられるか!という人は elm-minify とかを使いましょう。GZIP 後のサイズも表示してくれるので便利です。(ちなみにcreate-elm-appも同じ方法で minify しています)

$ elm-minify elm.js

┌────────┬────────────────────┬────────────┐
│ source │ rel path │ kb size │
├────────┼────────────────────┼────────────┤
│ input │ elm.js │ 87.28 │
│ output │ elm.min.js │ 6.667 │
│ gzip │ │ 2.735 │
└────────┴────────────────────┴────────────┘

簡単ですね!