ゆるゆる Elm

これはゆるく Elm をまとめた記事

関数や C 系言語にない演算子だらけだけど文法とかは解説しない
でも [公式ガイドの和訳](https://guide.elm-lang.jp/) は貼っておく
べっ、別にアンタのためにってわけjy////

[前に React+Redux でやったやつ](https://qiita.com/RkEclair/items/c35e969d3358d3ba2e2b) を、
Elm で実装してみただけ
別に前のを見ていないくても問題ないよ

# 下ごしらえ

作業するフォルダに `cd` したら早速公式ツールの `elm` で使う材料を用意する

```sh
elm init
elm install elm/browser
elm install elm/html
elm install elm/random
```

そしたら `src/Main.elm` の中に書いていく

手洗った?エディタ洗った?

私は VSCode を使う
この前洗いすぎて壊れたので再インストールした (`make clean` でうっかり `rm -rf /`)

# はろわるど

The Elm Architecture という考え方があります

> ```
>                [  ランタイムシステム  ]
> 　         　　Sub↓ 　            　↓↑Cmd
> [Update(更新コマンドの処理)] - [Model(そのモジュールでの状態)]
>       新しい Model↓　              　↑更新 Msg
>    　　      [     View(仮想DOMとか)     ]
> ```
> The Elm Architecture の超簡略図

とりあえずこのスケルトンを書いていきます
※React + Redux を先にやっちゃったので命名とかがそれに寄っています

```elm:Skelton.elm
import Browser
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (..)
import Random

main = -- ↓は規模に応じて sandbox, element, document, application にできる
  Browser.element
    { init = initialState
    , subscriptions = subs
    , update = reducer
    , view = view
    }

-- MODEL
type alias State =
  {diceFaces: Int}

initialState : () -> (State, Cmd Action)
initialState _ =
  (State 6, Cmd.none)

-- UPDATE
type Action
  = Roll
--                                      これ↓はreducer自身へ再送される
reducer : Action -> State -> (State, Cmd Action)
reducer action state =
  (state, Cmd.none)

-- SUBSCRIPTIONS - 今回は使わないのでこのまま
subs : State -> Sub Action
subs state =
  Sub.none

-- VIEW
view : State -> Html Action
view state =
  text "Hello, World!"

```

# 要件

詳細に定義してはいけないので頭を悪くして書きます(　ﾟдﾟ)

* ダイスを振るボタンがありゅ！
* 振るダイスを変更するとこがありゅ！
* 振ったダイスを表示しゅりゅ！
* 振ったダイスの履歴が残りゅ！

それじゃこれをさっきの、

* MODEL
  * 振るダイスの数
  * 振るダイスの面
  * 履歴
* UPDATE
  * ダイスを振り、↓ のメッセージへ転送
  * ダイス結果の追加
  * モデルのレコード (ダイスの数、ダイスの面) の変更 (2つ)
* VIEW
 * nDn の入力
 * ダイスを振るボタン
 * 振ったダイス履歴

の順番で書いていくぜ

# 実装

## MODEL

```elm:Main.elm
type alias State = -- Model のこと
  { diceAmount: Int
  , diceFaces: Int
  , history: List (List Int)
  }

initialState : () -> (State, Cmd Action)
initialState _ = 
  ( State 1 6 [] -- 1D6 がデフォルト
  , Cmd.none
  )
```

状態の型と初期状態を書くのでこれだけ。
ちなみに、Elm には

```elm
{ a
, b
, :
}
```

というコーディングルールがある

## UPDATE

こちらで更新メッセージを定義し、
VIEW やコマンドからの更新メッセージを処理する

`type` で書くカスタム型を使うと、分岐は `case ~ of ~` 式でできる

```elm:Main.elm
type Action -- Msg のこと
  = Roll
  | AppendResult (List Int)
  | ChangeDiceAmount Int
  | ChangeDiceFace Int

reducer : Action -> State -> (State, Cmd Action)
reducer action state =
  case action of
    Roll ->
      ( state
      , Random.generate AppendResult -- AppendResult へ乱数を送るコマンド
        <| Random.list state.diceAmount -- 複数回の乱数生成
        <| Random.int 1 state.diceFaces -- [1, 面の数] で乱数生成
      )
    AppendResult result ->
      ( {state | history = [result] ++ state.history}
      , Cmd.none
      )
    ChangeDiceAmount amount ->
      ( {state | diceAmount = amount}
      , Cmd.none
      )
    ChangeDiceFace faces ->
      ( {state | diceFaces = faces}
      , Cmd.none
      )
```

## VIEW
```elm:Main.elm
view : State -> Html Action
view state =
  div []
  [ inputView state
  , button [onClick Roll] [text "Roll"]
  , div [] <| List.map resultView state.history
  ] --        ↑の引数は JS とかと違って 関数 リスト の順

inputView : State -> Html Action
inputView state =
  let
    readNumber = Maybe.withDefault 1 << String.toInt
  in
    div []
    [ input
      [ alt "Amount to roll"
      , value <| String.fromInt state.diceAmount
      , onInput (ChangeDiceAmount << readNumber)] []
    , text "D"
    , input
      [ alt "Faces to roll"
      , value <| String.fromInt state.diceFaces
      , onInput (ChangeDiceFace << readNumber)] []
    ]

resultView : List Int -> Html act
resultView result =
  let
    join = text << (\str -> (++) str ", ") << String.fromInt
  in
    div [] <| List.map join result
```

# まとめ

これでシンプルなダイスアプリができた

ここでは一つのファイルにしてるけど
大きくなったら別ファイルに分けて `module` でもいけるはず

## それぞれの責務と追加できる機能

登場した概念について表にまとめておきます
こっから極めて真面目です(・_・)ｽｯ

え？今までも全然ゆるくなかったって？

| 概念 | 責務 | 追加/変更できる機能 |
| --- | --- | --- |
| Model | 状態の表現 | 新しいレコード(あまり変更されるべきではない) |
| Update | 状態に変更を加える | 状態が変わるロジック |
| Cmd / Sub | 副作用 | 副作用のある処理(関数の中に閉じ込めている) |
| View | 表示 | 見た目とそれを実現するための割り振り/データ加工 |

## Elm に対する所感

まぁ意外と使えるぽい
初心者にやさしいコンパイルエラーメッセージとか実行速度とか
なぜか言語仕様に関するドキュメントが見当たらないけど

# 追記

ページ単位くらいでモジュールにするのはいいけど

Model View Update は密なのでこれらをモジュールにしてはいけないと思う

というか公式ドキュメントにそう書いてた