Open58

elm勉強メモ

hayaohayao
hayaohayao

record

recordは他言語でいうdictionaryに近い
key:valueの集合

hayaohayao
hayaohayao

DOM

elm 関数呼び出しで表現
React 基本 JSX
Vue 基本 HTML

hayaohayao

Elm runtime

ElmがコードをJavaScriptに変換するときElm Runtimeという追加のJavaScriptを含める。

ランタイムがやること

  • イベントリスナーの追加・削除
  • HTTPリクエストやDOMの更新などのタスクの効率的なスケジューリング
  • アプリケーションの状態の保存と管理
hayaohayao

Elm アーキテクチャ

view はmodelを受け取ってHtmlを返す関数

hayaohayao
hayaohayao

update

  1. Eventが発生する
  2. elm runtimeがEventをハンドリングして、メッセージを生成する。
  3. update関数に渡される

updateはMsgを受け取ってmodelを返す関数

hayaohayao
hayaohayao
hayaohayao
hayaohayao
hayaohayao
hayaohayao

コマンド

コマンドとは、Elm Runtimeが実行する操作を記述した値です。関数の呼び出しとは異なり、同じコマンドを複数回実行すると異なる結果になることがあります。

update関数でコマンドをElmランタイムに送りつけると、ランタイムが所望の処理を実行して、Msgを返してくれる。

hayaohayao

<| 演算子

String.toUpper   (String.reverse "hello")
String.toUpper <| String.reverse "hello"
hayaohayao

|>演算子

String.toUpper (String.reverse "hello")
String.reverse "hello" |> String.toUpper
hayaohayao

作用

作用とは、外部の状態を変更する操作のことです。実行時に外部の状態を変更する関数は、副作用を持つ。

hayaohayao

Httpリクエストを送る

Httpモジュールを使って、Cmdを生成していた。
Browser.elementのinitプロパティ?にCmdを渡していた。

hayaohayao
hayaohayao

Custom Elements

Javascript側

javascriptでカスタムエレメントをつくる。
イベントを追加する。
ブラウザに登録する。

Elm側

Html eventのonで登録する。
カスタムイベントはMsgに変換しなくちゃいけない

道具

  • field
  • at

あとはupdateがMsg受け取って、Modelを更新。viewが更新されたモデルを使って描画を更新のいつもの流れ

hayaohayao

Port

JavaScriptの関数は副作用を持ちうる
Elmの関数は副作用を持たない
→ElmでJavaScriptの関数は呼べない
→Cmdを使う

Elm側

特殊な形式のport関数でJavaScriptにデータを送る。なにもMsgを受け取らない

JavaScript側

app.ports.~~.subscribe(data => なにかする)

hayaohayao
hayaohayao

サブスクリプション

サブスクリプションは、プログラム外の特定のイベントを、アップデート関数に送信されるメッセージに変換する方法を表します。

hayaohayao

フラグ

フラグとは、JavaScriptからElmプログラムのinit関数に渡される値です。

hayaohayao

整理したいこと

  • Decoderについて
  • HTTPについて
  • Cmd Subについて
  • Portについて
  • Custom Elementについて
  • Routingについて
    • Parser
  • SPA
  • テスト
hayaohayao

定石?

type alias Modelでmodelを定義する

type Msgでmsgを定義する。

view関数はHtml msgを返すsub-view関数群で構成していく

機能を追加したいとき

  1. イベントでCmdを送ってElmランタイムにMsgを送ってもらう。
  2. updateでハンドリングしてModelを更新する。
  3. Modelの更新に反応してviewが適切にHtml msgを生成するようにする
hayaohayao

number appendable comparableなど予約済み型変数
型クラスの代用?

hayaohayao

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という関数を呼び出して実現する。

hayaohayao
hayaohayao

よく出てきそうな課題

JsonとModelの対応付け
インビーダンスミスマッチ
Decoderの役割

hayaohayao
hayaohayao

SPA

Routing

hayaohayao
hayaohayao

Elmプロジェクトの始め方

ディレクトリを作ってelm init

hayaohayao
index.html
<!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
hayaohayao

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を用意する。

hayaohayao

プログラミングElm

1章

関数定義の書き方はHaskell的
関数は第一級だよ
カリー化できるよ

プロジェクトはディレクトリを作ってelm initとやるよ

"main"モジュールがエントリーポイント

elm make ファイル名でindex.htmlを生成

静的型はバグを発見とかしてくれて心強いよ

hayaohayao

2章

  • elmアーキテクチャー
  • レコード
  • カスタム型 type
  • イミュータブル

レコード更新構文

{ d | age = d.age + 1}
hayaohayao

3章

型エイリアス
静的型付けなのでリファクタリングしやすいよ
まず動かす、その後改善のステップをおすすめするよ

ボトムアップでコメント欄を実装

hayaohayao

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

hayaohayao

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関数とメッセージを登録することでメッセージを待ち受けることができるようになる。

<< 演算子

hayaohayao

6章

リファクタリング

テクニック

  • view関数の分割
  • メッセージの整理
  • モデルを入れ子にする
    • 状態の入れ子は煩雑になるので、使わないか、最大1階層までがおすすめ。
  • メッセージのパラメータをうまく使う
  • 再利用可能な補助関数を定義して重複をなくす
  • 複数の状態フラグの単一の型で表現する

Elmのon~がそこに渡されている関数になにを渡すかはon~の種類によって決まる?
例) onCheckはBoolなど

拡張可能レコード

hayaohayao

7章

Debugモジュール

<|演算子

優先順位が低い
主にカッコの代わりに使う
Haskellの$みたいな?

開発・deployの効率化

ちょっとふるいかもね

  • Elm reactor
  • create Elm App
  • parcel
hayaohayao

8章

Reactアプリケーションでelmのコンポーネントを使う

port

コマンドまたはサブスクリプションを返さなければならない

コマンドを返すポートは
外を向いたポート
サブスクリプションを返すポートは
内を向いたポート

tt = Elm.xxx.initする。
tt.elm.ports.yyy.subscribeでElmからJavaScriptにデータを送る
tt.elm.ports.zzz.sendでJavaScriptからElmにデータを送る

フラグ

hayaohayao

9章

テスト

アプリケーションのテスト

update

view

  • あるタグがある属性を持っているかのテスト
  • イベントのテスト
    • タグを選択してイベントをシミュレート、期待通りのメッセージがくるかを検証する
hayaohayao

10章

SPA

  • Browser.applictionを使う
  • onUrlRequest
  • onUrlChange
  • ModelにPageとNav.key持たせる
  • Routing
  • メッセージをtopレベルupdateで扱えるようにするためのHtml.mapやCmd.mapを使った調整
hayaohayao

11章

パフォーマンス
elm-benchmark
Html.Lazy 遅延評価

hayaohayao

elmって任意の型が=で比較できるのか?
List aと型注釈を書いてもaは=で比較できた。。

hayaohayao

Pageの種類

https://package.elm-lang.org/packages/elm/browser/latest/Browser

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