パステル色な日々

気ままに綴るブログ

Elmはどうやって超小さいアセットを実現したのだろう

Elmはとても小さいアセットを作ることができる

We had a community member working on a 49,315 line application try the new version and he got 114kb after compilation, minification, and gzip. That is in the ballpark of the 100kb produced by the 2000 lines in the Vue implementation!

blog/small-assets-without-the-headache

リンク先の画像が示すように、Elmはリリースされた0.19で十分に小さいJavaScriptファイルを生成することができるようになりました。RealWorldアプリケーションでの検証結果ですが、ひと目で主要なフロントエンドのライブラリ利用時よりも小さくなっている事がわかります。

0.19リリース時に公開されたこのブログ記事を見て僕は「すごいなー」と感心しました。一体どうやってこの小さなファイルを生成することができるようになったのでしょう。

デッドコードを削除する

僕はしばしば課題に直面すると同じような課題に対してアプローチしているライブラリがないか探す事があります。JavaScriptにはNPMという素晴らしいシステムがあるので、簡単にプロジェクトにライブラリを導入できます。

しかし、ライブラリのコードをすべて利用することは殆どないのです。ライブラリが公開しているAPIはどれも魅力的なものですが課題を突破するためにはいくつかのAPIで事足りる事が多いからです。

デッドコードはこうしたときに生まれます。利用してないコードがあってもライブラリをインストールした以上、ビルド時には無視できない存在となっています。もちろんライブラリだけではなくプロジェクト内のコードだって同じように考えることができるでしょう。

巨大なプロジェクトでは利用されてないコードが生まれることを見落としていることもあるかもしれません。webpackに代表されるようにツリーシェイキングによってこのようなデッドコードは削ぎ落とされることになりますが、完璧に削ぎ落とせてないそうです。

どうやらwebpackはモジュール単位でルーチンを走らせており、モジュールの粒度が大きくなればなるほど利用してないコードが紛れ込む可能性が高くなります。直接APIを呼んでない場合でも別のルーチンから呼ばれている場合もあるためライブラリが増えれば増えるほどデッドコードは増加する傾向にあります。

対策としてlodashに代表されるように関数ごとにモジュールを分ける方法があるものの、JavaScriptではどのモジュールでも実行時に関数の変更ができるので今呼ばれてないからと言って簡単に削除することはできないのが実情です。

しかし、Elmの関数は実行時に再定義と削除ができないため、メイン関数から到達可能であれば残して到達できなければ削除することができます。これがElmがアセットを小さく保つことができた1つ目の理由です。

また、Elmの場合はNPMとは異なり、package.elm-lang.orgでライブラリを公開しており、ここで公開されているライブラリはすべてElmで書かれているので安心して同じように削除できるのです。

レコードフィールドをリネームする

Elmにおけるレコードとは次のようなものです。

student =
  { name = "Haruka"
  , age = 17
  }

フィールドとはnameageを指していいます。これを参照する場合はstudent.nameとなりますが、0.19ではコンパイルの前後でs.nとリネームされるのです。

驚くべきことにこれで5% ~ 10%縮小するそうです。やはりこれもライブラリを含めたエコシステム全体で機能できます。

サマリ

  • Elmは0.19で主要なライブラリのどれよりも小さいJSファイルを生成する事ができる
    • デッドコードを削除する
    • レコードフィールドをリネームする

所感

非常に小さなアセットの生成が可能になった背景には言語の特性とその特性沿ったエコシステムがありました。一方でElm 0.19には多くの破壊的変更が含まれています。

compiler/0.19.md at master · elm/compiler · GitHub

コンパイラの最適化にはこのような変更を盛り込むしかなかったのでしょうか。Elmは純粋な関数型プログラミングの土壌を用意してくれていますが、JavaScriptに変換する際に問題を作り出してしまうこともあったのではないでしょうか。

変更の裏にはこうした課題を突破できないのでやむを得ず適用した変更したものもあるかもしれません。世界観を一貫して守りきる思想の強い言語という雰囲気を感じ取ることができました。それにしても破壊的変更が多くて大変です。