Elmメモ
Elmを入門して幾らか経ち,学びがあったのでこの記事にまとめます.書き忘れがあったら随時内容を追加していく.
elm-jp というDiscordサーバーがあり,この記事にはそのサーバーで教えてもらったことが幾つか含まれている.感謝します.
コードのうち,文脈から推測できそうな部分は省略している.
Http.Event
由来のメッセージで文字列以外の値を渡す
セレクトボックスなどフォーム由来のメッセージの場合,どういう文字列が得られるかは予め分かっている場合が多い.
更新関数内でその文字列をパースするのもいいけど,どういう入力があり得るのかが明に分かっている場所の近くでそういうのはしたい.
下の様に書くと入力文字列のパースを早い段階にすることで上を達成できるし,メッセージの型を文字列より具体的にできるのでミスを減らせる.
onChange : (String -> msg) -> Attribute msg onChange handler = on "change" (Json.Decode.map handler Html.Events.targetValue) type Msg = Hoge | Fuga | Piyo view : Model -> Html Msg view model = select [ onChange (\str -> case str of "hoge" -> Hoge "fuga" -> Fuga "piyo" -> Piyo _ -> Debug.todo "<invalid input>")] [ option [value "hoge"] [] , option [value "fuga"] [] , option [value "piyo"] []]
Elm Guide などを見ていると onChange
や onInput
にはカスタム型として定義されたメッセージのフィールドしか渡せなさそうな気がしていたが,全然そんなことはなかった
既存CSSフレームワークを使う
Elmには elm-ui (https://package.elm-lang.org/packages/mdgriffith/elm-ui/latest/) というのがあってこれでElm内でUIをいじることができる.
しかし僕はデザインにまったく自信がないので,適当なクラス名をつけておけば誰からも文句の出ないデザインになってくれるCSSフレームワークを使いたい.
これを解決するのは実は簡単で,ElmはJavaScriptを吐き出すことができるので,その吐き出されたJSを埋め込んでelmの初期化関数を呼び出してやればいい.
僕はspectreというCSSフレームワークを使っているのでこうなる
<!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <title>TITLE</title> <link rel="stylesheet" href="https://unpkg.com/spectre.css/dist/spectre.min.css"> <script src="main.js"></script> </head> <body> <div id="elm"></div> <script> var app = Elm.Main.init({ node: document.getElementById('elm') }); </script> </body> </html>
ここの main.js
は以下で生成できる
$ elm make src/Main.elm --output=main.js
ただしこの方法だと毎回 elm make
でJavaScriptを生成しないと elm reactor
が反応してくれないのが不便.
複数の制約付き型変数
Elmには制約付き型変数というのがある.これは型変数に特定の識別子を使うことで,型変数に代入される型がある性質を持つことを保証してくれるというものだ.
例えば comparable
という型変数を使うとそこには Int
や String
など,何かしらの自然な比較演算子があるものだけが代入される.
では,比較可能な型変数を複数個使いたい場合はどうすればいいのか,というとどうやら comparable1
や comparable2
でも comparable
と同様に比較可能な型変数として扱ってくれるようだ.
この仕様が言及されている公式ドキュメントは見つからず,これが使われている実装も BiDict
ぐらいしかヒットしないが頭の片隅にいれておいていい知識だと思う.
参考
Parcel でまとめる Elm と css と js のススメ https://qiita.com/kyasu1/items/44e67b8755f1adcfef67