Closed kachick closed 1 year ago
Elmに関しては 2年ぐらい前に @pankona さんとペアで4時間ぐらい?
さすがにもうちょっとやったような気がする…? 当時よりも我々にはフロントエンド力 💪 がついているはずなので、いまやったらもっと "理解る" かもしれない…!
@pankona6~8時間 ぐらい
に書き換えておいた!
当時よりも我々にはフロントエンド力
フロントエンド力どうこうよりも、関数型言語のところで挫折した記憶があるからあんま関係無い気がするんだよなぁw フロントエンドに関しても、Reduxは苦手なままだし・・・ でも、小規模だからなのか、同じファイルの中に Redux 的な概念が小さく詰まっていると言うだけでも全然違うねー。今の所ずっと感触が良い。ただ、Reduxも非同期通信が混ざってきて redux-saga とか redux-thunk 使う辺りからなんだこれになってきたらからまだ油断は出来ない気がしている。
読んだ。グレート!
前にやったときは、枠を赤くするみたいのはやったような記憶があるね?手元にソース残ってるかな...?というかどっかにコミットしてたような...? https://github.com/pankona/elm-architecture-tutorial これだっけ...?
@pankona なにかのリポジトリをクローンしたりせずgudeだけに則ってやってた気がするから、それは自分で独習したんじゃない?w
とは言うものの、自分も謎のリポジトリを2年前に作っている
https://github.com/kachick/learn_Elm_todo => Todoを手習いにするつもりだったのに、もう作ってた・・・!? https://github.com/kachick/learn_Elm_command => CLIか?と思ったら、なんか外部のエンドポイント叩いている気配があるのでその練習っぽい
いやー、当時せっかくかけた時間がお互い無駄になって感が否めないですなw ちゃんと手に馴染ませないと・・・
他の変換を追加することができますか。華氏から摂氏?インチからメートル?
重い腰を上げてやることにしたけれど、両方やる気力もわかないので インチ -> メートル変換だけ追加しようと思った。 換算定義はぐぐればすぐ出てくる https://kenkou888.com/category21/inch_m.html が・・・詰まった。 View の変更は簡単だ、なんとなく span をコピーして div で括って間に br を挟めばとりあえず動く。
import Html exposing (func)
の定義が、 vscode の quick fix から auto import した時に既存の定義へ追加してくれないのが不便だけれど、別にそれで動かないというわけでもないみたいなのでいっかと思った。
model への フィールド追加も別に迷うことはない
動作確認も、 elm reactor を使ってサクサクである。この辺りの環境構築の楽さも TypeScript + React + Redux 辺りと比べると段違いだ。一応コンパイルされた JavaScript のコードも軽く眺めてみたけれど、まぁ所謂 AltJs とは毛色が違うのはそういうもんだろうなーと思った。
問題は update の部分だ。 2つのフィールドに対して両方が maybe の時にうまいことパターンマッチを書く方法があるのかよくわからない。 何も考えずにやるなら 1つのパターンマッチの中に次のパターンマッチをネストして・・・とやればなんとかなるだろうけど、さすがにしんどいしそんなの嫌だ。 しかしそうすると、ここはどう表現すれば良いんだ・・・? 基本的な話だという気がしていてここは放置しちゃいけない部分だろうなとは思いつつ、かなり解決に時間を食いそうな予感がするので一旦飛ばしてしまう。多分このまま進むとどっかで詰まるだろうから、そこで改めて対処したい
使いすぎを避ける
nullable に対してパターンマッチを強制するので堅いが、それよりもそもそも型エリアスを駆使したほうが良いですよ。という理解をした。忘れないようにしたい。これは TypeScript でも使える考え方なのではないか?
bad
type alias Friend =
{ name : String
, age : Maybe Int
, height : Maybe Float
, weight : Maybe Float
}
good
type Friend
= Less String
| More String Info
type alias Info =
{ age : Int
, height : Float
, weight : Float
}
https://guide.elm-lang.jp/error_handling/result.html
Result
type Result error value
= Ok value
| Err error
なんとなくこういう型エリアスなんだろうなーというイメージは掴めていると思うのだけれど自信はない。 バリデーションエラーの表示分けが例に使われている。
次の章では実際にHTTPリクエストの作り方を見せていきます。なのですぐさまResult型とError型に再会することになりますよ!
お、大事なところへ入っていくなー。なんかやっぱそろそろ詰まりそうだけれど、サーバー通信介さないTODOとかだったらここまでの知識量で一応作れそうな気がするし、一旦 guide から離れるのも有りかもしれない。
https://guide.elm-lang.jp/effects/
Browser.sandbox
そうなんだよねー、ここが結構パターンがあって、例によく使われる Browser.sandbox はシンプルコード用で複雑になってくると別のを選ばないと行けないような理解は2年前にもしてた気がする。
続くいくつかの例ではプログラムを作るためにBrowser.elementを使っていきます。そこでは外の世界とのやり取りを可能とするためのコマンドとサブスクリプションの概念を導入していきます。
なるほどー。SPAとかだと最低限これは必要になりそうだな。
このプログラムではHtmlという値に加えて、CmdとSubなる値をランタイムシステムに送り出します。ここでは我々のプログラムがランタイムシステムに対してHTTPリクエストを送ったり乱数を生成するよう指示することができます。また現在時刻をランタイムシステムから待ち受けて我々のプログラムで使うこともできます。 いくつかの例を見ていくと、コマンドとサブスクリプションについての理解が深まると思いますので、やってみましょう!
Note 3:「指示する」を意味する英語のCommandがCmdの語源です。英語のSubscriptionがSubの語源で、主に定期購読と訳されますが、Elmでのイメージは外部から来るメッセージを定期的に待ち受けるところからから来ています 。
あー、 https://github.com/kachick/learn_Elm_command の 「command」 はこれを指してたっぽいな。何も覚えてないけども、この辺りまでは2年前にも進めてた部分という事だと思う・・・
https://github.com/kachick/times_kachick/issues/143 が終わったので、この issue の goal を Todo から https://github.com/mobu-of-the-world/mobu の少機能クローンを作るところにしようと思った。(https://github.com/kachick/times_kachick/issues/86 へ書いたやつ)
が・・・ List の shuffle で詰んだ。Ruby だったら Array#shuffle
一発なやつだが、どうやら elm だと core から今は Random モジュールが切り離されていて、尚且そいつが Generator とか言うのを返すようになっているらしく単純置換ではいかない・・・
え、こんなところでこんな難しそうな事させられるの!?と思ったが、これは必要な「マインドセット シフト」らしい。敢えて注釈がついているということは、ここで引っかかる自分のような入門者がかなり多いのだろう
https://package.elm-lang.org/packages/elm-community/random-extra/latest/Random.List https://package.elm-lang.org/packages/elm/random/latest/Random#generate https://guide.elm-lang.jp/effects/random.html https://github.com/elm-community/random-extra/blob/d52055975644ad401709c2aff14dab9ca93e44a0/tests/Tests/Random/List.elm#L13-L24 https://package.elm-lang.org/packages/elm/random/latest/
Note: Read through guide.elm-lang.org to learn how commands work. If you are coming from JS it can be hopelessly frustrating if you just try to wing it. And definitely ask around on Slack if you feel stuck! Investing in understanding generators is really worth it, and once it clicks, folks often dread going back to Math.random() in JavaScript.
Mindset Shift
If you are coming from JavaScript, this package is usually quite surprising at first. Why not just call Math.random() and get random floats whenever you want? Well, all Elm functions have this “same input, same output” guarantee. That is part of what makes Elm so reliable and easy to test! But if we could generate random values anytime we want, we would have to throw that guarantee out.
So instead, we create a Generator and hand it to the Elm runtime system to do the dirty work of generating values. We get to keep our guarantees and we get random values. Great! And once people become familiar with generators, they often report that it is easier than the traditional imperative APIs for most cases. For example, jump to the docs for Random.map4 for an example of generating random quadtrees and think about what it would look like to do that in JavaScript!
Point is, this library takes some learning, but we really think it is worth it. So hang in there, and do not hesitate to ask for help on Slack or Discourse!
なんとか乱数パートを乗り換えた。完全に理解したとは言い難いけど、 https://qiita.com/ababup1192/items/47ebac90daf3d7a26f56 を読んで、なるほど https://github.com/kachick/learn_Elm_command でやった Cmd やなという事でこれを書き換えていった。
他に学んだこととしては https://qiita.com/mather314/items/1d5917b2b9f415d18372 の パイプライン演算子(Pipeline Operator)
・・・と言いたいが全く分かっていなくて読み飛ばした。
後は無名関数の書き方 https://sporto.gitbooks.io/elm-tutorial/content/jp/01-foundations/02-functions.html と、 List.filter https://package.elm-lang.org/packages/elm/core/latest/List#filter
次に localStorage で四苦八苦した。TypeScript の時は https://github.com/mobu-of-the-world/mobu/pull/20 みたいに cookie を使った。自分が望んだというより @pankona さんの要求どおりにした機能だけど、なるほどこれが無いとリロードの度にデータが吹っ飛んで、利用というよりも開発がしづらい 😅 で、今回も同様にしようかなと思って https://github.com/elm-lang/cookie を見に行ったら
Cookies are an ancient mistake of web browsers. They make a bunch of security problems quite easy, so consider this an expert library.
If you are looking for a way to store information, you should take a look into local-storage or session-storage instead.
はい、すみません・・・ よく違い分かってないんだよねということで調べてみたら、session-storage は揮発性が高くて local-storage は割と長期保存向きという雑な解釈。 で、 elm で localStorage の使い方を調べてみると・・・でた、いきなり port! なるほど、これが https://www.kabuku.co.jp/developers/the-case-for-mint でも言われていたような「基本的なことまでport使わされる」ってやつか・・・?確かにここで必須になるのは辛いものがあるなぁ。 https://github.com/billstclair/elm-localstorage を見たけどアクティブな更新されてないようだしなんかベタ書きの方がマシそうな気配があったので、取り敢えず下記の資料あたりを見様見真似でやってみた。
https://guide.elm-lang.jp/interop/ports.html からまず参照されている https://github.com/elm-community/js-integration-examples/tree/master/localStorage へ行き、関連コードをほぼそのまま使わせてもらった。E とか D で import するの気持ち悪くないのでそのままにして https://package.elm-lang.org/packages/elm/json/1.1.3/ を読みながら・・・
しかし decoder の定義がよくわからない。 Json.Decode.map2 とやらは 2 filed のときなんでしょ?だから Json.Decode.map で十分では・・・と差し替えてみるもコンパイルが通らない。また map2 でも 1引数目を list の decoder にするとダメだけど、順番変えると通るみたいなかなり謎な状況になってしまった。しかしまぁ全部 storage に突っ込んじゃえば良いやということでとりあえずそのままにしたらコンパイルは通った・・・が、実際には localStorage に入らない。
ここで https://guide.elm-lang.jp/interop/ports.html を読み直すと、より上の階層として https://guide.elm-lang.jp/interop/ があった。なるほど、index.html へのダイレクトなコンパイルは諦めて main.js にコンパイルし、index.html 側に ports の JavaScript 側コードを書けと・・・なるほど確かに動いた。動いたけど、これって elm reactor
使えなくなるよね・・・? なんか、かなり基本的なところから「嬉しさが目減りしていく」感があってちょっと辛い・・・が、まぁ、取り敢えず動いたので良しとするかー。 https://kachick.github.io/mobu-elm/ へ auto deploy するようにした。 repository は https://github.com/kachick/mobu-elm commit は https://github.com/kachick/mobu-elm/commit/f6f2ff5a01cab18cee183b2eb84f3f308a4ec832
index.html へのダイレクトなコンパイルは諦めて main.js にコンパイルし、index.html 側に ports の JavaScript 側コードを書けと・・・なるほど確かに動いた。動いたけど、これって elm reactor 使えなくなるよね・・・? なんか、かなり基本的なところから「嬉しさが目減りしていく」感があってちょっと辛い
とりあえず elm-live という外部ツールを使えば解消した。 起動のさせ方というかパスの扱いを間違えると
internal/fs/utils.js:620
throw new ERR_INVALID_ARG_TYPE(propName, ['string', 'Buffer', 'URL'], path); ^
TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string or an instance of Buffer or URL. Received undefined
とかいうエラーになる
elm-live src/Main.elm --open --start-page=docs/index.html -- --output=docs/main.js
これは起動はするが、main.jsの相対pathの扱いが間違ってしまっていて実際には動かない
ので、ここに動いたやり方をメモる
$ npm install --global elm elm-live@next
$ cd ./docs
$ elm-live ../src/Main.elm --open -- --output=main.js
elm-live:
Server has been started! Server details below:
- Website URL: http://localhost:8000
- Serving files from: /Users/kachick/repos/mobu-elm/docs
elm-live:
The build has succeeded.
elm-live:
Watching the following files:
- **/*.elm
elm-live
, --hot
つけても hot reload 効かなかった・・・こうなると手動で make した方が楽という自体に。辛い。
その後割と集中して色々やったから、逆にログに何を残せばいいか思い出せないところが多いのだけれど、commit log みながら思い出して書く
https://github.com/kachick/mobu-elm/commit/c06307ab83ac6ad0bbd460b62d38ebd0f8365356 でかなり基本部分の実装を終えた。
Json.Decode.map2 とやらは 2 filed のときなんでしょ?だから Json.Decode.map で十分では・・・と差し替えてみるもコンパイルが通らない。また map2 でも 1引数目を list の decoder にするとダメだけど、順番変えると通るみたいなかなり謎な状況になってしまった
これを map に直せた。が、詳しい理屈を知るのは更に後
timer に関しては https://guide.elm-lang.jp/effects/time.html の subscription を見様見真似で使ったらうまく行った。結局時間を持つと機能的に扱いづらかったので、カウンターを持つ感じにしちゃったけど
let~in, Maybe, List.Extra.swapAt 辺りもここで初めて使ったかなー。Maybeは初めてじゃないけど、頭を使ったのは初めてというか・・・
https://github.com/kachick/mobu-elm/commit/ec9edd7d382cda4a7626fb3d1d8505e113dc7091 をやるために、 https://github.com/kachick/mobu-elm/commit/4ec70f59a64ae22ed4f28db7fb3882c9798ffbd7 で username だけじゃなく User というカスタム型?で表現するようにした。リファクタリングに安心感があるというのはそれはそうなんだけど、それでもやっぱ大変だったので、この手のデータは最初からちょっと拡張性持たせといた方が良いかも・・・ ここで改めて JSON の decode/encode に向き合う必要が出てきたのだけれど、 https://qiita.com/canisterism/items/3faafe384fb0d5c95708 がめっちゃ参考になった。これが無かったら挫折してたかもしれない・・・分かってみれば簡単というか気持ちがいい感じだ。これも所謂関数合成?
onError
は Task だのなんだの求めてきて面倒だったので、 on "error"
というのを使うことにした。違いはよく分かってない。 https://www.debugcn.com/ja/article/172783617.html の怪しい翻訳サイトっぽいのが役立った。
https://github.com/kachick/mobu-elm/commit/6c698fe1e409cfa634273ecbb7e7207a611c031e で input tag に number やら min, max といった attribute の渡し方を学んだ。 https://stackoverflow.com/questions/33857602/how-to-implement-a-slider-in-elm が役立った。
見た目というかCSS以外の機能で残ってるのは https://github.com/mobu-of-the-world/mobu/pull/79 と https://github.com/mobu-of-the-world/mobu/pull/13 かな? 手間掛かりそうならここで一旦 done でも良い気はしてきたが・・・
JSON の decode/encode 周りがかなり手探り感あったことと、コンパイル通ればOKと言い切れない部分だったので unit test の書き方を学ぶ良い題材だろうとしてやってみた
結果 => https://github.com/kachick/mobu-elm/commit/f76b03cc5c56dc4dac6f11f8f3a918f945dfba6f
まず https://github.com/elm-explorations/test を npm 経由で入れる必要がある。やたら ⭐ が少ない気はするけど test 系ツールが不遇なのはどこも一緒なのだろう。
test の書き方としては https://elmprogramming.com/easy-to-test.html と https://becoming-functional.com/testing-json-decoders-in-elm-39f613a98895 が参考になった。
GitHub Actions でのCIやり方としては https://stackoverflow.com/questions/60058556/using-elm-test-in-a-git-hub-action が役立った。ただ、container 上だと package-lock.json
に差分が出てしまうようなのがちょっと気持ち悪かった。
1 file で済んでた気持ちよさみたいなのがモジュール分割する必要出てきてちょっと残念だったけど、型を中心にモジュールへ切り出すと良いような感じの事が書いてあったので取り敢えずやってみた https://guide.elm-lang.jp/webapps/modules.html
後は Maybe 使ってる所に Maybe.withDefault とかいう便利そうなのが使えるかな?と思ったけど微妙にフィットしなかったので次の機会に回す。 他、今List使ってるけど用途的には Array の方が正しいかもなというのと、elm の Array には swapAt が無いみたいなので追加してみてようかなというのと、パイプ演算子をもっと活用できるか調べてみようかなと思った。 Array と パイプ演算子 に関しては 基礎からわかるElm を読んで惹かれた感じ。
https://github.com/mobu-of-the-world/emobu
CSS 整えるところ面倒で放置してたんだけど、grid template でざざっと分割してCI周り整え直す等して組織へ移管した。 第一部完! https://github.com/mobu-of-the-world/mobu/discussions/775
動機
「色んな職場でつぶしがきく」的発想だと Elm を選ぶ理由は特に無いと思うのだけれど、「自分1人で作って、その後も楽にメンテしつづけたい」みたいな時には React apps 系の dependency 更新地獄にならないっぽいし、 TypeScript よりも堅く書けるという意味で美味しいのでは?と思った。
開始時点での Elm 力
そもそも Frontend 自体弱い上に、Elmに関しては 2年ぐらい前に @pankona さんとペアで6~8時間 ぐらい? https://guide.elm-lang.jp/ を読んでサンプルアプリを立ち上げて、ちょっとカスタマイズの問題やろうとしたら、すぐに引っかかって挫折した。 個人では 基礎からわかる Elm には一通り目を通して良い感じだなーと思ったんだけど何も覚えてない・・・
ぐらいなんだけど「感触は良かった」
参考資料
ログ
2021-06-26
まず https://qiita.com/arowM/items/5ec5853298fc880353b7 が最新版に更新し続けられてるっぽいので軽く目を通して、一応 discord に入っておくことにした。 beginners とかいうチャンネルが合って如何にも良さそうなので、とりあえず何か詰まったら今度はここで聞いてみたい https://discord.com/channels/388648490173202434/395574575783608321 そこで紹介されていた https://github.com/lucamug/elm-ecommerce を見て良さげだったので ⭐ 付けた。こういうのポンポン作れるようになりたい・・・しかし CSS 0 とか書いてあるけど、じゃあどうやって作ったんだろう?謎技術だ。
https://qiita.com/arowM/items/dfb38d1c5f3dfde8b8bf を読んだ。逐一納得できる。やはり自分に合うのでは・・・?
言語基礎的なところはむしろ飛ばしたかったんだけど、実践を謳っている guide でも最初にあるのでやむなく触ってみた。
https://guide.elm-lang.jp/core_language.html
ボリュームは少ないので、たしかに触って正解だと思う。 書かれては居たが、エラーメッセージの詳細さには本当に感心する。ArgumentError だったらたしかにこう表示して欲しいな〜みたいなのも全部満たしてくれて気持ちがいい。 Ruby でいうところの did_you_mean 的な物も当たり前のように用意されていた。
https://guide.elm-lang.jp/architecture/buttons.html
薄い言語機能の基本をなぞった後は、いきなり The Elm Architecture に入る。さすがだなーという感じで、コンセプトはわかるものの、「Redux にめちゃくちゃ苦手意識がある」自分にその本家は受け入れられるのだろうか?
とりあえず https://guide.elm-lang.jp/architecture/buttons.html の練習問題をやってみる https://elm-lang.org/examples/buttons に リセット機能を足せというもので、いきなり躓いていきなり解決した。 ポイントは
update msg model =
のところで、
model = 0
としたのがつまづきだった。なるほど、自分の頭が関数型になってないわけだなふむふむと考えてupdate : Msg -> Model -> Model
の定義を見た上で、そういえばこの Model って単に Int のエリアスだっけと気づいた。
type alias Model = Int
ということで、
0
と、単に 0 を返すだけのコードに変えたらコンパイルが通ってちゃんと動いた。いきなり躓いたのはあれだけれども、一瞬で解決できたのでむしろ気持ちがいい絶対こういうこと言いたいわけじゃないんだろうなーとは思いつつ、
Increment10
という message を増やして解決してしまった。多分あれでしょ、本当は message に一緒に引数を渡せるように出来て、それを学んでほしいんでしょ?と思いつつ見て見なかったことにする。後で困ったら調べよう。https://guide.elm-lang.jp/architecture/text_fields.html
ということなので見てみた。オンラインエディタから簡単にジャンプできる事自体は嬉しい物の、 日本語ガイド -> 本家オンラインエディタ -> 本家ガイド という遷移になってしまうようで、若干面倒というか、わかってないと頑張って英語読む羽目になってしまいそうだ(ちょっと読んでしまった)。大した手間でも無いので、とりあえずは都度都度対応する日本語ガイドを探して読むようにする。
さて、ここではさっきの疑問への答えというか、メッセージに引数を渡せそうな雰囲気だ。やったー!という感じでさっきの Increment10 を Increment に step を渡せるように変えてみようとしたが・・・うまくいかない。
onClick Increment 1
いつもの自分なら挫折しそうだが、ここでエラーメッセージの詳細さによって即座に助けられた。
Or missing parentheses?
様様である。気持ちがいい。ちょろすぎでは?と思ってやってみたら速攻で失敗した。全く学習していない見積もりの甘さである。
なるほどなーということで、Hint様の導くまま
とやってみた、が怒られた。これは多分関数呼び出しの引数のルールあたりを自分がわかってないんだろうなぁと当たりをつけて適当にいくつか試してみた結果以下で通った
一瞬悩んで一瞬で解決の繰り返しで気持ちのいいチュートリアルだ(まぁ、数年前に同じことやってた筈なのでアレだが・・・)
これはね、ここで脱線というかそっちに気を取られるとそれで1日終わっちゃうから敢えて見ない。
https://guide.elm-lang.jp/architecture/forms.html
お、いきなり複雑というか、 view 周りに手が入ったなという印象。そしてその解説がちゃんとされている
JSXが使えない事は色々言われているようだし作者も意図的だと返事をしているらしいが、記法はともかくやりたいことは似たようなもんなのかな?と思った。まぁやっぱJSXの方がHTMLっぽく読めるよなぁとは思いつつ、慣れればこれでもいけそうな気がするのでとりあえずそのまま進めよう。
げ、自分の苦手な
短い命名推奨文化
か? と思ったけど、多分単に関数名とのバッティングを避けてるのだと思うのでこれも深く考えないで置く。いい話だ。
ふむ・・・
まず
パスワードが8文字より長いか確認する。
これは、 String.length を既に知っているわけなので、知りたいのは関数どうこうというよりもelsif ってどうすんの?
だった。 これはガイドには書かれていなかったので、公式っぽいドキュメントをあたった。 https://elm-lang.org/docs/syntaxelse if
が正しいらしい。それだけわかれば話は解決で、以下を先頭に入れて他を else if にすることで実現できた。こいつは確かに機能を知らないと大変だろうなと思って https://package.elm-lang.org/packages/elm/core/latest/String を見に行った。 薄いと言うよりもシンプルというか必要十分な感じで、全部目を通すのにもさほど時間はかからない。 ここだけ見ると、正規表現系の機能がなさそうで、他モジュールに切り出されているのか使わない方針なのかはおいおいわかるだろうと考えて今は追わない。 indexes の alias として indices が用意されているのには、え、そここだわっちゃうの?と、ちょっとウッとなった。でもまぁこれ一個ぐらいしか alias 見当たらないし、気にしないようにする。
さて使えそうな機能は
contains : String -> String -> Bool
toList : String -> List Char
any : (Char -> Bool) -> String -> Bool
かなと辺りをつけたが、どれを選べば良いのか選定がちょっと悩ましい。 contains で
大文字、小文字、数字
を調べるのに全パターンの String というか Char を書けなんて言うのはキツイ話なのでまず捨てる。 そうすると Char の List にしてしまうとマシか?と思った。ただこれも、全パターンの Char を網羅する必要はありそうでやはり面倒くさそうだ。そうすると結局 any に落ち着く気がする。 例示されている isDigit とかいう関数は単なる例なのか本当にあるものなのか調べてみたら、本当に Char にあるものっぽい
https://package.elm-lang.org/packages/elm/core/latest/Char
isUpper と isLower もあるので、これで解決かなという気分。
だったのだけれど、今度は「マッチしなかった時」分岐がほしいので、 unless というか boolean を反転させる機能を知らないと駄目ではと気づいた。
!
を頭につけたがコンパイルエラーだったので https://elm-lang.org/docs/syntax を調べる。 invert とか unless で探したが出てこなかったので bool で探したら答えを見つけたなるほど not ね。今度こそ解決だ。
と思ったら 単に isDigit を使うと そんなの無いよと怒られる。ただこれは予期してたので驚かなかった。むしろそうなってほしいというか。 import の仕方とかも出てきたが、単に Char.isDigit で、今度こそ解決
any 1回で 複数のチェックを巻き取れたほうが効率が・・・みたいな発想はあんましない方が良いだろうし、ここでは考えない。
あれ、なんで急にHTTPリクエストの話がここで出てきたんだ?とはちょっと思ったけど、バリデーションの中でサーバーに問い合わせるような用途を気にしてかなと思った。まぁ普通避けて通れない話だとは思うので、ガイドの中でカバーされてると知れただけでも安心感はある。
いやー良いこと言うなぁ・・・ホントそうだと思うよ。
https://guide.elm-lang.jp/types/
ここで型の話に入った。いきなり言語機能のところでここを深堀りせずに、ある程度実際のコードを触らせた後でここに持ってくるのがニクイところだ。
おっ、凄い自信満々では?ここがまさに TypeScript じゃなくて Elm を選びたいところの一つなんだけど、正直 JavaScript へコンパイルしている時点で
ランタイムエラーが実際に起きない
なんて言えるのか・・・?と疑問には思うのだけれど、信じてみようこの威力というか恩恵には既に十分預かっている。 🙏
さて、以下 typo 検出の強さをつらつら述べてくれているのだが、この辺になってくると「REPL欲しいな・・・」と思った。でぐぐったら https://elm-lang.org/news/repl が出てきたのだが、editor に vscode がなかったり記事が2013だったり諸々古くて怪しいので GitHub に飛んでみたら archive されとる https://github.com/elm-lang/elm-repl
Merged into elm/compiler
ということで https://github.com/elm/compiler に飛んで見る・・・が、READMEが敢えて薄い感じ。 https://elm-lang.org/try のオンライエディタへのリンクは出てきて、まぁ元々フロントエンドに特化した言語らしいから、むしろこっちになれるかと思った。これをREPLと考える! で、その結果出てきたエラーメッセージが大分ガイドと違うので、PRをどこかで投げてはおきたい。今それをやると脱線しちゃうからね・・・ぐっと我慢。https://guide.elm-lang.jp/types/reading_types.html
おー、ここまで強く勧めてくれるとむしろ気持ちが良い。そうしましょう。
ふむ、
List a -> Int
の a への説明っぽい。詳しい人には怒られそうであれだけど、 any じゃなくて generics 的なもの。と雑に理解しておく。お、短い命名に凄い強くこだわってる感が無くて性に合いそう。
なるほど。広げすぎないという感じっぽいので、自分でコード書くときもまずは制約付きで考えたほうが良いのかな?
ちょっと次のページ行くのが怖い
https://guide.elm-lang.jp/types/type_aliases.html
あ、なんだ。これぐらいならわかる。ほっとした。
これはちょっと不便感というか、名前付きでコンストラクト出来ない物かね・・・ まぁ一個のレコードが多くのフィールドを持ちすぎないようにしたり、型の恩恵に預かれることとか考えると気にしなくて良いのか・・・? うーん、保留。
大事。
https://guide.elm-lang.jp/types/custom_types.html
おっ、なんかいきなり難しくなった気がする・・・ まず、改善前例に上げられている方のコードは読める
改善例が、なんとなくはわかるが、諸々ショートカットされすぎてる気がして、あんましっくりこない・・・
しかし、次まで行くとメリット的な物がわかる気もした。
なるほどー、型によって持つフィールド自体が変わるというパターンで、変に nullable なパターンみたいなのを考慮しなくてよくなるからこっちの方がいい的な感じなのかな・・・わかる気もする。
なるほど・・・ "The Elm Architecture" の Msg こそが、まさにその例だと。なるほどだなー。
なるほどしかり
最も重要!やたら手厚いなぁと思ったらそこまでか。いやー、これは心に刻んでおかないと。 ✏️
https://guide.elm-lang.jp/types/pattern_matching.html
おっ、 カスタム型を case で分岐させる例だ
いやホントそうなんだよね、これが TypeScript & Redux だと never を使いましょうみたいにしかならない認識で、それでも結局実行時例外を拾いきれない事があったので、前に Elm をちょっと触った時「感触が良い」と思った部分の一つなんだよな・・・
ははぁ、 Ruby なりでも使わない変数名なりの頭に
_
つけると警告しないでくれるあの辺かな。 Elmの場合には別に使わなくてもコンパイルエラーにはならないような感じだけれども、_
を prefix にしたりも出来るのかな?出来ないっぽい。ちょっと残念。
https://guide.elm-lang.jp/error_handling/
お、気になるところではあるのだけれど、
ランタイムエラーを実際に見ることはない
なのか。本当なら凄いな・・・でたー! Maybe! Haskell 入門しようとした時に引っかかった部分の一つだった気がする・・・ ・・・が、まぁ、ここまでは別に理解できるか。言語機能というか組み込みとしての Maybe 的な話じゃなくて、単なるカスタム型での表現の話だもんな。
強い!頼もしい!
おー、ここでいよいよ組み込みの? Maybe が出てくるわけか・・・
https://guide.elm-lang.jp/error_handling/maybe.html
まじで・・・?ちょっと引いてしまうんだが・・・
シンプルそうに見えてよくわからんやつだ・・・
わからんが、ここで深堀りすると何も実用的なものを得られないまま終わっちゃう可能性が高いので、後から理解できる==未来の自分に期待して一旦先へ進む
ん、String.toInt も調べてみたら同じく Maybe を返すっぽいが、これ特に意識せず使わなかったっけ? と思ったら、あれは String.fromInt だった。そっか、String.fromInt が失敗する事はないというか型チェックで全部済むけど、 String.toInt なりはそうもいかないという事ね・・・それはそう。
うむ。。。謎である。
さて、ここで紹介されているリンクが https://ellie-app.com/bJSMQz9tydqa1 とか言うところに飛ばされて、なんらかの accept を求められて怪しい気がしたのでちょっとストップしてしまった。本家ガイドでもそうなのか?と比較してみたが同じリンクだった。ならいっか、と accept したら普通にオンラインエディタ系の画面が開く。これはこれで良いんだけど、他と違うのなんでだ?という疑問が・・・
ここでちょっと休憩がてら、昔買って読んですべて忘れてた 基礎からわかる Elm を開いてみる・・・と、イキナリ答えが出てきた。こいつは Elm 作者の同僚が作ったサイトらしく、WebAssembly で作られているんだと。ほうほうーと、そしてここでREPLについての答えも出てきた。本家のインストールすれば
elm repl
で開けるらしい。 ガイドだと、もうちょい先の章にインストールの説明がある https://guide.elm-lang.jp/install/elm.html これは、どういう流れで読者に読んでほしいかという思想の違いだと思うのだけれど、結果として並行読みに助けられる感じになった。 この手の言語はそうだろうなぁと思いつつ、REPLでは複数行への対応に\
を都度入れなきゃいけないのが面倒くさそうではあるが、やはりCLIから入力できるのは楽なので適宜使っていきたい 書籍の序盤を読んで感心したことのもう一つが「Elm はversion upに伴ってどんどん破壊的変更を入れてきたが、それはむしろ機能を削ぎ落としてシンプルさを追求してきた結果」というような事が書かれていてすげーなと思った。確かにいくつかのモジュールのリファレンス見てもあまり量が無かった。もう一つ脱線で、ぐぐったときに https://www.kabuku.co.jp/developers/the-case-for-mint を見つけた。 kabuku さんの事はあまり良く知らないのだけれど、 https://www.kabuku.co.jp/developers/good-bye-typescript-enum 等にはお世話になったし、フロントエンドに強いところなのだろうと思っていてちょっとこの Elm 評価には怖くなってしまったが、一旦あまり気にしないことにする・・・
で、 https://ellie-app.com/bJSMQz9tydqa1 に戻る。まず目を引いたのが html で
自分はどちらかと言うとSPA方面の技術力向上をそんなに狙っていないというか「自分には難しすぎる気がするので、個人プロジェクトではなるべく避けたい」と思っている方なので、ページ単位で別のコード使えるんなら良いなーと思ってたんだけど、それは Elm 的にどうなんだろ。 Routing を本当に必要かどうかはよく考えろみたいなのは見たけど、それはそのままSPA否定ということにはならんと思うし・・・まぁいいや、おいとく
これはリファクタリングでは無くてエンハンスでは!?とちょっと思ったが置いとく。
いやー、いかにも簡単そうなんだけど
この
style "width" "40px"
、よく見るとというか、癖強すぎでは・・・? しかしこういうときこそ Elm のユーザーフレンドリーなところで、 オンラインエディタから常にリファレンスへ簡単に飛べるようになっているのだ。https://package.elm-lang.org/packages/elm/html/1.0.0/Html-Attributes#style
このシグネチャ?は、まぁなんとなくそんな感じだろうなと思っていた。
なるほどー?複数 style を指定したいときは戻り値 の list にしろということなのかな。ま、なんとなく想像してたけどね!
基本使うな、CSSファイルなり使えと。まぁそらそうよねという気はする。
これを確認するには、むしろこの戻り値を受け取ってる側の定義を確認する必要があるなと思って見てみた
https://package.elm-lang.org/packages/elm/html/1.0.0/Html#input
そうっぽい。可変長引数とかありなん?と思ってたけど、そもそも順番気にしないと言うか、
Attribute msg
をいくつでも受け取ってうまいことさばいてくれる。と理解した。便利では?というとこで今度はフロントエンドという大枠以上に苦手意識のあるCSSの話になった
CSS input 枠線
でぐぐると怪しげな日本語サイトが上位に来てしまうので、CSS input border MDN
で探す。なんでもMDNに当たっとけばいいっしょぐらいの思考である。https://developer.mozilla.org/ja/docs/Web/CSS/border https://developer.mozilla.org/ja/docs/Web/CSS/border-color
まずは面倒くさい条件分岐のコード考える前に、本当にそれで色が変わるのか試してみる
・・・変わらない。というか、今気づいたけど青い。
style "border-corolr2" "red"
みたいに存在しない?プロパティ名を指定してみたがコンパイルは通ってしまう。なにー、コンパイルエラーにならんのかー!と一瞬思ったが、型定義的に正しいし、そもそもそれは別にこのレイヤーで確認するべき話と言うことではないのかなと思ったのでぐっとこらえる。何故青いのか?は、よくコードを見ると別のところで色指定、それもまさに
blue
がされているのでこれっぽいな・・・いや、それは算出した温度に対しての色変化っぽいから、枠線の色とは関係ない?そもそも枠線ってデフォルト青だっけ?とか混乱してきた。 とりあえず blue を black に変えると、枠線は青いままで温度のところが黒くなった。つまりそういうことだよなぁ・・・ え、じゃあ枠線はどうするんだ?
順番変えても反映されないしな・・・ 渋々StackOverFlowとかもみてみたけど、inputにこのプロパティが使えないこと無いような・・・ というところで気づいた。typo だこれ。
赤くなった。 むむむ・・・これは、どうなのだろう?一度は
別にこのレイヤーで確認するべき話と言うことではないのかなと思った
話なのだけれど、 React & TypeScript の style だと防げた話だと思うんだよな・・・https://ja.reactjs.org/docs/dom-elements.html#style
ふむー
いやーそうなんだよね、普通は外部のCSS使いましょうはわかりつつ、動的に計算されたスタイルを入れる的な話では使う機能なわけで、その時にここに type check が効かないのは単純に不便では? と思った。が、まぁ考えようによっては「CSS当たらなくても最低限使えるように作っとけ」とも言えるかなと思ったので、ここは自分の中で「要注意だな」とだけ覚えておいて、良否判断はしないでおく。
さて本題の条件分岐だけど「この条件のときだけ可変長引数をこうしたい」は Ruby だと簡単だと思うけれど、Elmではまだ習ってないなというか、なんか筋悪な気がする。というところでコードをちょっと広く見てみると、そもそも色渡しを既に外側でやっているのだからそれに乗っかれば良いのだと気づいた。
なんのことは無い、これだけで済む話だったのだ。しかしここに辿り着くまでにかなり時間を食った。40分ぐらい?
めんどくさ・・・と思ったが、一応やるかー?というか、この辺も確か数年前に通った道のはずなんだよな・・・と思ったけど
そういえばガイドの上部にずっとこんなのが出ていた。ということは、これも当時はちょっと違ったかも?でも摂氏華氏辺りはやった気もするんだよなー。ただ日本で暮らしていると「華氏もインチも縁がない」ので、そこを調べるところからしてやる気が出ない。
とりあえず
追加
というのが厄介だなと思った。丸々機能書き換えならinput
といったフィールド名もそのままでいけるだろうから楽だと思うんだけど、追加となると Model の定義からいじるわけだよね・・・こうなってくると、そろそろオンラインエディタというよりは慣れ親しんだ vscode を使いたいところださて、自分の vscode には昔入れた elm の機能が入っている・・・のだけれど、よく見ると deprecated になっている
https://github.com/elm-tooling/vscode-elm-old
今は https://github.com/elm-tooling/elm-language-client-vscode https://marketplace.visualstudio.com/items?itemName=Elmtooling.elm-ls-vscode を使えとのことなので、仰せのままに古い方をuninstallしてこっちを入れてみる。
で、適当に対象のコードを
.elm
というファイル名で保存して vscode で開いてみたが・・・ハイライトすら効かない ぐぐるとなんか他のアプリケーションで使われている拡張子みたいだから、避けてるのかな?と思ったが、 https://en.wikipedia.org/wiki/Elm_(programming_language) を見ると.elm
になっている。 とりあえず https://github.com/elm-tooling/elm-language-client-vscode の README から、入っていないツールを入れに行く変わらない・・・
あ、やっぱり(負け惜しみじゃないよ) elm.json が無いから?そんな気はしてたんだよねー。 こういうの作るの面倒くさそうだなと思ってたんだけど、
elm init
だけで良いと言うならやってみるか でやってみるとsrc
というディレクトリも出来たのでここに突っ込んで見る・・・が、やはりハイライト効かないぞ?https://guide.elm-lang.jp/install/editor.html を見てみると https://github.com/elm/editor-plugins のリンク集が紹介されていて、ここから飛べるのが https://github.com/elm-tooling/elm-language-client-vscode で、13日前に更新かかっている。やはりこいつが今の現役プラグインっぽいんだけれども。 なんか設定いるのかな?と思ってプラグインの説明をvscodeから開くと、なぜかinstallされていない・・・え、さっきしたはずなのに・・・?まぁここを深追いしてもしょうがないのでinstallしてみたらあっさりシンタックスハイライトが効いた。これは Elm 全く悪くないんだろうけどどっと疲れてしまった・・・
しかしこのプラグインかなり便利そうだぞ。 関数名にカーソル合わせるとちゃんと説明が出るし、定義元ジャンプも効く、F2での一斉renameも出来る・・・めっちゃ快適やん。Haskell の学習あまり気乗りしなかった理由がIDEの有力なやつが当時なさそうな事も大きかったんだけど、Elmで出来るんだったらHaskell でもできそうな気がするな・・・ と思ってググったら今は https://github.com/haskell/haskell-language-server とやらが有力らしい。というか自分 ⭐ つけてある。vscode にも対応していそうだし、今度もっかいチャレンジしてみるかー
というところで1日が終わった。