Fringe81 Advent Calendar 2018の21日目です。
忙しい人のためのネタバレ
Elmのコンパイラバージョンを上げてコンパイルが100倍早くなった
前回までのあらすじ
Elmで実装されたフロントエンドアプリケーションのコンパイル時間が半端なく長くなってしまった!
そこでヒープサイズを盛ることによって3倍くらい改善して、250秒くらいから30秒くらいにはなった!
前回のエピローグより...
0.19ではRTSオプション気にしなくても超早くなりました。
Road To 0.19
超早くなるといってもプロダクトのコードやってなかったのでやっていくぜ!
やる前のインプット
- https://github.com/elm/compiler/blob/master/upgrade-docs/0.19.md を読む
- https://github.com/avh4/elm-upgrade#elm-upgrade-- を読む
読み物によってインプットしたことを実施していく
- まずは
elm-upgrade
をポチッとな。 - あとは
elm-upgrade
が出してくれたあとはコレやってねリスト
にしたがっていく...
「ここからが本当の地獄だ...]
-
コレやってねリスト
-> 注文が多い - 自分らで作ってたイケてる演算子を抹消 & 関数呼び出しに置き換える
-
Date
モジュールに依存していたところをTime
モジュールを使っていい感じにするようによしなにする - CustomTypeに対してtoStringを使っていたところを専用のtoStringを定義してあげる
-
Html.style
を使っていたところをどうにかする -
elm/regex
の変更に対応するように変更する - Debug.crashを抹消する
- etc...
やること多いな!
モジュールが新しくなったり、関数の返す型が変わったりプロダクトのソースコードもかなり変更が必要だなというのがelm-upgrade
によってわかったところですべての対応を一気にやろうとするのは骨が折れそうなのでチェックポイントを設けて実施することにしました。
フロントエンドアプリの構成が当時は2つのElmアプリケーションからできていて、片方は1画面でもう片方が複数ページを扱うような感じでした。
なので1画面アプリケーションのほうから着手してみて問題がないこと、やりきれること、がわかったらもう片方も着手することにしました。
さらに、Debug.crash等のDebugモジュールは抹消しなくてもビルドやアプリケーションを動作させることは叶うので作業を分けてみることにしました。とくにDebug.crashはMaybe型を強制的にunwrapするために使ってたという事実があり抹消が困難だろうという目算がありました。
そこで立てた計画が以下になります
1画面アプリケーションの0.19移行
- `Html.style`等を0.18のころのように扱えるファサードの実装
- 各種ライブラリのバージョンアップ追従のための変更
- Debugモジュールの抹消
*↓は↑が行けたら行く *
複数アプリケーションの0.19移行
- 各種ライブラリのバージョンアップ追従のための変更
- Debugモジュールの抹消
↑を実施する上での戦いを紹介しておきます↓
Html.style
を使っていたところをどうにかする
-
もうElmからあんまstyleを定義すんなよ! とHtml.Attributes.style関数のドキュメントにあるようにもうあんまやっちゃだめなんだな!とわかりつつ、今までElm側でstyleを指定していたものをcssに分離するのは大変だなあという甘えがあったので以下のような関数を定義して
逃しました。
styles : List ( String, String ) -> List (Html.Attribute msg)
styles =
List.map (\( propname, propvalue ) -> Attr.style propname propvalue)
使う側としては
-- elm 0.18 ver
viewStyle = style [("width", "100px")]
view = div [ viewStyle ] []
-- elm 0.19 ver
viewStyle = styles [("width", "100px")]
view = div ([] ++ viewStyle) []
美しくないな!やCSSかけや。と感じつつ一旦コンパイルを通すという目標のために逃げ
ました。
真似せずCSSを書いてクラス名でしたほうがいいですね!
Dateモジュールに依存していたところをTimeモジュールを使っていい感じにするようによしなにする
DateモジュールがリストラになってTimeモジュールを使ってネということになったので
Date型でフィールドを定義していたレコードはすべてTime.Posix型に変更しました。
yyyy-mm-ddとして表示するようなタイムゾーンに依存していたところはasia/tokyo
のZone型の値を用意しておくような対応をしました。
-- 0.18時点
formatYYYYMMDD : Date -> String
-- 厳密にやると以下のようにしたいところですが。(Time.yearやTime.monthがZone型を取るため)
formatYYYYMMDD : Zone -> Time.Posix -> String
-- 関数の中でZone型の値はすべて`asia/tokyo`のものを勝手に渡すものとして以下のように定義していきました。
formatYYYYMMDD : Time.Posix -> String
プロダクト的に表示される時間はすべて日本時間でも問題なかったので↑のような逃げ
方ができました。
elm/regex
の変更に対応するように変更する
elm/parser
を使ってネ!と丁寧にelm/regex
のREADMEで書かれていた気がするが、オラア正規表現が使いたいんや。プロダクトのコードを変更させんでくれ。というお気持ちでelm/parser
は華麗にスルーしたところで
elm/regex
の変更へ追従していく変更を開始しました。
Regex.regexという引数に文字列をとるRegex型のファクトリ関数があったので今まで以下のようにしていました。
someRegex = Regex.regex "なんか正規表現文字列"
0.19からはRegex.fromStringという関数に変わりました。
someRegex = Regex.fromRegex "なんか正規表現文字列"
しかしこの関数Maybeを返すようになりました。
関数に渡した文字列が正規表現として間違っていたらNothingで返してくれるというなんとも便利な仕様です。
が、0.18のころからコードをあまり変えたくないというワガママな私には困ったものでした。
ということで↓のように関数を定義してここでも逃げ
ました。
fromRegexUnsafe regexString =
Regex.fromRegex regexString |> Maybe.withDefault Regex.never
ここまで作業をするとelm make
は実行できるようになります。
そしてここからは--optimize
フラグを利用するためにDebugモジュールを使わないようにしていきます。
Debug.crashを抹消する
お恥ずかしいところですが、
Maybe型やResult型の値を強制的にとりだすためにDebug.crashを使っていました。
-- 真似しちゃだめよ!!!!!
sure : Maybe a -> a
sure maybeValue = case maybeValue of
Just value -> value
Nothing -> Debug.crash "ありえないんだぜ"
ここは地道にこういうことをしている関数がすべてMaybeを返す or Resultを返すように定義していきました。
とくにサーバサイドとのコミュニケーションをするようなコードではコレが蔓延していたのでとても辛い戦いになりました。
すべての作業が終わった結果
before(0.18)
npx elm-make src/Main.elm --output app.js 384.90s user 136.40s system 355% cpu 2:26.53 total
before(0.18 & https://qiita.com/4245Ryomt/items/20712197c9b4b76f0f5b)
npx elm-make src/Main.elm --output app.js 59.28s user 13.68s system 128% cpu 56.635 total
after(0.19)
npx elm make src/Main.elm --output app.js 1.88s user 0.34s system 65% cpu 3.380 total
圧倒的改善...!
300倍以上早くなってますね!
これで生産性も300倍!!!
# やり終わって
- 0.18と0.19が混在していた時期に手の混んだwebpackのコンフィグ作ったりココに書き切れないくらい苦労はあった。
- バージョンアップで苦労したところってこれはイイのかな。。。ホントは良くないんだろうな。。。と思っていたところだった。
- Debug.crashをデバッグ以外の用途で使う
- Date型を直接持つ(タイムゾーンを意識しないで時間をどうにかするアプリをつくることも)
結構学びはあった。
ご注意
複数ページアプリは0.19にして問題もでた。--debug
フラグをつけてコンパイルできなくなっちゃう〜というコンパイラのバグに遭遇してelmの便利開発ツールが有効にできてない。
300倍のコンパイル速度と天秤にかけて、そのうち治してくれるだろうということで今は我慢している。
https://github.com/elm/compiler/issues/1817#issuecomment-440194948