Elmわからんのでcounterサンプルに全部コメント入れた

直してみよう。

f:id:nekorollykk:20200819130906j:plain
Elmっちへ
ういっすー!
関数型言語が 全く わからなくて、
つらみが 深いので、
1行ずつ コメント入れていきます。
アプリは 書けたら書くマンです!

ellie-app.com

1ブロックずつメモしていく

exposing / import

scrapbox.io

-- main関数をMainモジュールとして公開
-- var app = Elm.Main.init({ node: document.querySelector('main') })
module Main exposing (main)

-- Browserモジュールをインポート
import Browser
-- HtmlモジュールからHtml型, button/div/text型をインポート
import Html exposing (Html, button, div, text)
-- Html.EventsモジュールからonClick関数をインポート
import Html.Events exposing (onClick)
  • main関数をElm.Main.initでHTMLに出力するためにexport
  • HTMLの描画に使用するBrowser.sandboxを使うためにインポート
  • HTMLを構成するbutton, div, textを使用するためにHtml型と関数をインポート
  • buttonの要素onClickを使用するためにHtml.Eventsからインポート

至って普通のインポートなので特に難しい所なし

type Model

-- Int型のcountにAliasでModelという型名をつけて定義
type alias Model =
    { count : Int }
  • Int型のcount変数(レコード)にModelという型名をつけて持っている
  • 頭文字大文字は型である
  • ElmだとModelは状態らしいですね
  • こんなイメージ
<?php
class Model extends int {
  int count;
}

このコードだとメリットわからないけど
type alias User = {id : Int, name : String}
とかだとメリットある。
構造体的なイメージ

initialModel関数

-- Model型のinitialModelを定義
initialModel : Model
-- count=0でセット。型はModel(count :int)で定義
initialModel =
    { count = 0 }
  • initialModel : Model
    こいつは型定義
  • initialModel = { count = 0 }
    こいつが実処理

function initialModel(Model $model) な感じ
コンストラクタ的なイメージ(実際initで使われている)

Msg型

-- Msg型を定義。配入るのはIncrement / Decrementのみ
-- ここで言うIncrement/Decrementは型ではなく値
type Msg
    = Increment
    | Decrement
  • 真骨頂 Msg型という独自の方を定義
  • 入るのはIncrement / Decrementという値(型?)
    typescriptでもhoge = int | stringって有ったよね
  • MsgはElmだとなんか色々受け渡して詰め込んでるらしい
    Msg = アクション的なイメージ

update関数

-- update関数 Msg Modelを引数にModelを返す
-- function update(Msg msg, Model model): Model 的な
-- 省略できる(型推論)けど、書くのが正しい
update : Msg -> Model -> Model
-- Msg msg, Model model
update msg model =
-- if (msg === Increment) elseif (msg === Decrement) 的な
-- returnはない。そのまま返り値になる(カリー化とか部分適用とか知らん、いらん)
-- パターンマッチってやつらしい
    case msg of
    -- Incrementの時
        Increment ->
        -- model(count:int)を+1で更新する、レコードなのでこの構文
            { model | count = model.count + 1 }
    
    -- Decrementの時
        Decrement ->
        -- model(count:int)を-1で更新する、レコードなのでこの構文
            { model | count = model.count - 1 }

急に難易度上げるのやめてほしい、初心者殺し

  • function update(Msg msg, Model model): Model
    っぽいけどその実カリー化されているらしい ナマステ
    • update(Msg msg)を呼ぶとfunction(Model model)が返ってくる(謎)
    • x, yの座標処理でxを固定しつつyに対して処理する~とかで活きるかも
    • つまりこんなことが出来る
-- numberとnumberを受け取ってnumberを返す
add : number -> number -> number
add : a b = a + b
-- add: a b のaが1に固定された関数が作れた
add1 = add 1
-- bを2で呼び出す
-- これで3が返ってくる
add 2
  • パターンマッチは割と見たまんま
    渡したMsg msg = Increment | Decrementを処理する
  • { model | count = model.count +1 }
    この書き方はmodelのcountにmodel.countを取り出して+1した値を代入している
    レコード取り出すのはこういう記法らしい

view関数

-- view関数 Modelを引数に(Html Msg)のtupleが返る
-- function view(Model model): tuple(Html html, Msg msg) 的な
view : Model -> Html Msg
-- Model model
view model =
-- (div[]がHtml, [...]がMsg) なのかな…
    div []
    -- 要素 属性 要素の中身
    -- div [onclick] [text] みたいな感じ
    -- なので今回はdiv 要素なし 中身がネストしてbutton [] []という形になっている
        [ button [ onClick Increment ] [ text "+1" ]
        , div [] [ text <| String.fromInt model.count ]
        , button [ onClick Decrement ] [ text "-1" ]
  • Modelを引数に受けて(Html Msg)のtupleを返すみたいなイメージ
  • div[] button とかがHtml
  • onClickの Increment/DecrementがMsgらしい
    どういった経路でココに繋がるのかさっぱりわからん

main関数

heimdal.hatenablog.com

-- main関数 Program型 フラグなし Model Msg
-- function (Program program, Model model, Msg msg): void
-- 第2引数はflagでなにもないことを示しているらしい
-- https://heimdal.hatenablog.com/entry/elm-0.19-sandbox#sandbox
main : Program () Model Msg
main =
    Browser.sandbox
        { init = initialModel
        , view = view
        , update = update
        }
  • initで起動時に動く関数を指定
  • viewで描画する関数を指定(恐らくHtml Msgになるんだと思う)
  • モデル更新処理で動く関数updateを指定

わからんところ

  • update関数はどんな流れでviewのIncrement/Decrementと繋がっているのか
  • view関数のMsgはどうIncrement/Decrementと繋がっているのか

まとめ

input outputがよくあるアプリケーションと違うので全くわからん
写経したり動かすぐらいなら出来るけど、ちゃんと理解してないのですぐ詰まる
誰か助けてくれ