elm勉強メモ
record
record
は他言語でいうdictionary
に近い
key:value
の集合
Collection
- List
- Record
- Tuple
DOM
elm 関数呼び出しで表現
React 基本 JSX
Vue 基本 HTML
Elm runtime
ElmがコードをJavaScriptに変換するときElm Runtime
という追加のJavaScriptを含める。
ランタイムがやること
- イベントリスナーの追加・削除
- HTTPリクエストやDOMの更新などのタスクの効率的なスケジューリング
- アプリケーションの状態の保存と管理
Elm アーキテクチャ
view はmodelを受け取ってHtmlを返す関数
update
- Eventが発生する
- elm runtimeがEventをハンドリングして、メッセージを生成する。
- update関数に渡される
updateはMsgを受け取ってmodelを返す関数
コマンド
コマンドとは、Elm Runtimeが実行する操作を記述した値です。関数の呼び出しとは異なり、同じコマンドを複数回実行すると異なる結果になることがあります。
update関数でコマンドをElmランタイムに送りつけると、ランタイムが所望の処理を実行して、Msgを返してくれる。
<| 演算子
String.toUpper (String.reverse "hello")
String.toUpper <| String.reverse "hello"
|>演算子
String.toUpper (String.reverse "hello")
String.reverse "hello" |> String.toUpper
作用
作用とは、外部の状態を変更する操作のことです。実行時に外部の状態を変更する関数は、副作用を持つ。
Httpリクエストを送る
Httpモジュールを使って、Cmdを生成していた。
Browser.elementのinitプロパティ?にCmdを渡していた。
JavaScriptと話す
- custom elements
- port
Custom Elements
Javascript側
javascriptでカスタムエレメントをつくる。
イベントを追加する。
ブラウザに登録する。
Elm側
Html eventのonで登録する。
カスタムイベントはMsgに変換しなくちゃいけない
道具
- field
- at
あとはupdateがMsg受け取って、Modelを更新。viewが更新されたモデルを使って描画を更新のいつもの流れ
Port
JavaScriptの関数は副作用を持ちうる
Elmの関数は副作用を持たない
→ElmでJavaScriptの関数は呼べない
→Cmdを使う
Elm側
特殊な形式のport関数でJavaScriptにデータを送る。なにもMsgを受け取らない
JavaScript側
app.ports.~~.subscribe(data => なにかする)
サブスクリプション
サブスクリプションは、プログラム外の特定のイベントを、アップデート関数に送信されるメッセージに変換する方法を表します。
フラグ
フラグとは、JavaScriptからElmプログラムのinit関数に渡される値です。
テスト
elm-test init
でset up
整理したいこと
- Decoderについて
- HTTPについて
- Cmd Subについて
- Portについて
- Custom Elementについて
- Routingについて
- Parser
- SPA
- テスト
定石?
type alias Model
でmodelを定義する
type Msg
でmsgを定義する。
view関数はHtml msgを返すsub-view関数群で構成していく
機能を追加したいとき
- イベントでCmdを送ってElmランタイムにMsgを送ってもらう。
- updateでハンドリングしてModelを更新する。
- Modelの更新に反応してviewが適切にHtml msgを生成するようにする
number appendable comparableなど予約済み型変数
型クラスの代用?
Monad
class Monad m where
(>>=) :: m a -> (a -> m b) -> m b
return :: a -> m a
>>=
は例えばmがMaybeならMaybe a を a -> Maybe b に食わせるという操作。
これに相当するをElmではandThenという関数を呼び出して実現する。
よく出てきそうな課題
JsonとModelの対応付け
インビーダンスミスマッチ
Decoderの役割
SPA
Routing
Elmプロジェクトの始め方
ディレクトリを作ってelm init
<!doctype html>
<html>
<head>
<style>
</style>
</head>
<body>
<div id="app"></div>
<script src="app.js"></script>
<script>
Elm.AppName.init({node: document.getElementById("app")});
</script>
</body>
</html>
elm make src/AppName.elm --output app.js
SPA
Browser.applicationを使う
onUrlRequest
ページが遷移する前に必ず呼ばれる
case msg of
ClickedLink urlRequest ->
case urlRequest of
Browser.External href ->
( model, Nav.load href )
Browser.Internal url ->
( model, Nav.pushUrl model.key (Url.toString url) )
onUrlChange
URLが変わった直後に呼び出される
init関数を呼び出したり、データをサーバーに問い合わせたりする
モデルにPageとNav.keyを用意する。
プログラミングElm
1章
関数定義の書き方はHaskell的
関数は第一級だよ
カリー化できるよ
プロジェクトはディレクトリを作ってelm init
とやるよ
"main"モジュールがエントリーポイント
elm make ファイル名でindex.htmlを生成
静的型はバグを発見とかしてくれて心強いよ
2章
- elmアーキテクチャー
- レコード
- カスタム型 type
- イミュータブル
レコード更新構文
{ d | age = d.age + 1}
3章
型エイリアス
静的型付けなのでリファクタリングしやすいよ
まず動かす、その後改善のステップをおすすめするよ
ボトムアップでコメント欄を実装
4章
JSONデコーダ
elm install
で依存関係の導入
パイプライン演算子
elm/json
Decoder
Decoderを作る
- succeed
- required
- hardcoded
パターン
succeed 引数取ってレコード返す関数
|> required 第一引数に対応するプロパティの名前 第一引数に対応するプロパティの型に対応する関数
|> ...
Httpモジュール
Http.get { url = url, expected = Http.expectJson MsgVariant Decoder }
browser.element
Cmd
Subscriptions
Maybe
5章
- port
- subscription
javascriptとやり取りする。。
app.ports.XXX.subscribe(YYY)
XXXはZZZ -> Cmd msgなport関数
YYYにZZZを渡す
app.ports.XXX.send(YYY)
XXXは(ZZZ -> msg) -> Sub msgなport関数
YYYはZZZと型が一致しなくてはならない
subscriptionでport関数とメッセージを登録することでメッセージを待ち受けることができるようになる。
<< 演算子
6章
リファクタリング
テクニック
- view関数の分割
- メッセージの整理
- モデルを入れ子にする
- 状態の入れ子は煩雑になるので、使わないか、最大1階層までがおすすめ。
- メッセージのパラメータをうまく使う
- 再利用可能な補助関数を定義して重複をなくす
- 複数の状態フラグの単一の型で表現する
Elmのon~がそこに渡されている関数になにを渡すかはon~の種類によって決まる?
例) onCheckはBoolなど
拡張可能レコード
7章
Debugモジュール
<|演算子
優先順位が低い
主にカッコの代わりに使う
Haskellの$みたいな?
開発・deployの効率化
ちょっとふるいかもね
- Elm reactor
- create Elm App
- parcel
8章
Reactアプリケーションでelmのコンポーネントを使う
port
コマンドまたはサブスクリプションを返さなければならない
コマンドを返すポートは
外を向いたポート
サブスクリプションを返すポートは
内を向いたポート
tt = Elm.xxx.initする。
tt.elm.ports.yyy.subscribeでElmからJavaScriptにデータを送る
tt.elm.ports.zzz.sendでJavaScriptからElmにデータを送る
フラグ
9章
テスト
アプリケーションのテスト
update
view
- あるタグがある属性を持っているかのテスト
- イベントのテスト
- タグを選択してイベントをシミュレート、期待通りのメッセージがくるかを検証する
10章
SPA
- Browser.applictionを使う
- onUrlRequest
- onUrlChange
- ModelにPageとNav.key持たせる
- Routing
- メッセージをtopレベルupdateで扱えるようにするためのHtml.mapやCmd.mapを使った調整
11章
パフォーマンス
elm-benchmark
Html.Lazy 遅延評価
elmって任意の型が=で比較できるのか?
List aと型注釈を書いてもaは=で比較できた。。
Pageの種類
SandBox
sandbox :
{ init : model
, view : model -> Html msg
, update : msg -> model -> model
}
-> Program () model msg
Element
element :
{ init : flags -> ( model, Cmd msg )
, view : model -> Html msg
, update : msg -> model -> ( model, Cmd msg )
, subscriptions : model -> Sub msg
}
-> Program flags model msg
Document
document :
{ init : flags -> ( model, Cmd msg )
, view : model -> Document msg
, update : msg -> model -> ( model, Cmd msg )
, subscriptions : model -> Sub msg
}
-> Program flags model msg
type alias Document msg =
{ title : String
, body : List (Html msg)
}
application
application :
{ init : flags -> Url -> Key -> ( model, Cmd msg )
, view : model -> Document msg
, update : msg -> model -> ( model, Cmd msg )
, subscriptions : model -> Sub msg
, onUrlRequest : UrlRequest -> msg
, onUrlChange : Url -> msg
}
-> Program flags model msg