ElmのBrowserパッケージについて調べた

プロジェクトにてフロントをElmで、バックエンドは Haskell を使うことになりました。

[An Introduction to Elm](https://guide.elm-lang.jp/)

等を参考に勉強中です。

今回は自分の中での整理も含め、[Browser](https://package.elm-lang.org/packages/elm/browser/latest/) パッケージについてまとめます。

## 結論　Browserは目的に合わせて関数を選ぶ
`Browser`の４つの関数はそれぞれ引数レコードの型が異なります。
例えば既存のアプリケーションにElmで作成した要素を部分適用する場合には`sandbox`や`element`を、
一からSPA作ろうと思ったら`application`を選択するなど、
コンパイル後のhtmlやjsをどのように使用するか、またそのファイルでやりたいことに合わせて`sandbox`、`element`、`document`、`application`から選択して`main`関数を定義します。



## 基本的なElmファイルの構成
↓[ボタン・An Introduction to Elm](https://guide.elm-lang.jp/architecture/buttons.html)から拝借

``` elm
import Browser
import Html exposing (Html, button, div, text)
import Html.Events exposing (onClick)



-- MAIN


main =
  Browser.sandbox { init = init, update = update, view = view }



-- MODEL

type alias Model = Int

init : Model
init =
  0


-- UPDATE

type Msg = Increment | Decrement

update : Msg -> Model -> Model
update msg model =
  case msg of
    Increment ->
      model + 1

    Decrement ->
      model - 1


-- VIEW

view : Model -> Html Msg
view model =
  div []
    [ button [ onClick Decrement ] [ text "-" ]
    , div [] [ text (String.fromInt model) ]
    , button [ onClick Increment ] [ text "+" ]
    ]
```

Elmのガイドにて、真っ先にサンプルとして紹介されているプログラムとなります。
ガイドに詳しくは記載されていますが、Elmの基本的なパターンは

> 1. ユーザーからの入力を待ちます
> 2. updateにメッセージを送ります
> 3. 新しいModelを生成します
> 4. view関数を呼び出して新しいHTMLを取得します
> 5. 画面上に新しいHTMLを表示します
> 6. 繰り返します！

と記載されています。

ここで、今回の本題の`Browser`ですが、`main`関数で登場しています。
この`main`は、
> `main`はElmでは特別な値で、画面に何を表示するかを記述します。この例では、アプリケーションを`init`で初期化して、`view`関数ですべてを画面に表示し、ユーザーからの入力を`update`関数に渡します。これがプログラムの大まかな概要だと考えてください。

とのことで、作りたいアプリによって`main`を`Browser`を使って定義する必要があります。

## Browser
[Browser](https://package.elm-lang.org/packages/elm/browser/latest/)

`Browser`には関数が４つ用意されています。
- `sandbox` ：ボタンやチェックボックスなど入力への反応
- `element` ：HTTPやJSとの相互運用などの外界との通信
- `document` ：`<title>` と `<body>` を操作
- `application` ：SPAを作る

との説明でこれらを必要に応じて使い分けていきますが、それぞれの関数の型の差を見ていきます。


### Browser.sandbox

``` elm
sandbox :
    { init : model
    , view : model -> Html msg
    , update : msg -> model -> model
    }
    -> Program () model msg
```
`init`、`view`、`update`から、`Program`を返します。
今回は深追いしませんが、`Program`で型変数`flags`、`model`、`msg`をもとにプログラムの状態を返している、くらいの認識で進みます。
`()`はユニット型と呼ばれ、空の値が入るという認識で進みます。
`Browser`の関数の中ではレコードの要素が一番少ないです。

後述するflagsやsubscriptions等を使用する場合は、そもそも`sandbox`の引数の型に含まれていないため、
例えば、

``` elm
...
-- MAIN

main =
  Browser.sandbox
    { init = init
    , view = view
    , update = update
    , subscriptions = subscriptions
    }
...
```
はコンパイルが通りません。

### Browser.element

``` elm
element :
    { init : flags -> ( model, Cmd msg )
    , view : model -> Html msg
    , update : msg -> model -> ( model, Cmd msg )
    , subscriptions : model -> Sub msg
    }
    -> Program flags model msg
```
先ほどの`sandbox`と比べ、
`Cmd`、`flags`、`Sub`という要素が追加されています。
概要の説明となりますが、elementでは、下記４通りの方法で外界と通信することが出来ます。
一つのHTML elementを返します。それを別のJSのプロジェクトに追加したりできます。
- `Cmd` ：HTTPなど、Elmランタイムに対し命令を出せます。
- `Sub` ：Websocketでつないだ先からのメッセージの購読など、イベントを"subscribe"できます。
- `flags` ：初期化と同時にJSからElmに何らかの値を渡します。
- `ports` ：JSとの相互運用でクライアント-サーバーの関係を設定できます。

### Browser.Document

``` Elm
document :
    { init : flags -> ( model, Cmd msg )
    , view : model -> Document msg
    , update : msg -> model -> ( model, Cmd msg )
    , subscriptions : model -> Sub msg
    }
    -> Program flags model msg
```
`element`と比較すると、`view`関数で`HTML msg`ではなく、`Document msg`を返しています。

`Document`は型エイリアスで、

``` elm
type alias Document msg =
    { title : String
    , body : List (Html msg)
    }
```
と定義されており、`Browser.element`では単一のHTMLの要素を返すのに対し、
`Browser.document`では"title"と、"body"を含めたコントロールを返すことが出来ます。

### Browser.application
``` Elm
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
```
`document`と比較すると、`init`の型に`Url`と`Key`の引数、
`onUrlRequest`、`onUrlChange `が追加されています。
これらにより、URLの変更を管理できるアプリケーションを作成します。

`init` では、アプリ起動時に最初のURLを取得します。
`onUrlRequest` では、リンクが押下された際に実行する関数を`update`関数内に設定できます。
`onUrlChenge` では、Urlが変更されたときに実行する関数を`update`関数内に設定できます。


## 終わり
プロジェクト、フロントの構成ってどうしよ、とか考えながら見ていたのですが、
[モジュールの構造化](https://guide.elm-lang.jp/webapps/structure.html)で面白いことが書かれてました。
> 防衛本能
The Life of a Fileで私は、その人がElmへと入ってきたときに迷子にさせてしまうような、JavaScriptの神話的知識について指摘しました。

> ・~~『ファイルは短くしよう』~~ JavaScriptでは、とてもやっかいなバグを引き起こす、隠れた状態変化が起きやすいです。しかしElmでは、そのようなことは起こりえません！ファイルは2000行の長さになってもいいですし、それでもそのようなバグは起きないのです。

> ・~~『最初から正しいアーキテクチャにしよう』~~ JavaScriptではリファクタリングはとてつもなく危険で、最初からすべて書き直したほうが手っ取り早いでしょう。でもElmでは、リファクタリングは簡単で安全です！20の異なるファイルでも自信を持って変更することができます。

> これらの『防衛本能』は問題からあなたを守ってくれると思うかもしれませんが、そんな問題はそもそもElmには存在しないのです。これらの神話を頭の片隅に置いたままにしても、防衛本能に振り回されるよりはいいでしょう。でも、ファイルが400行、600行、800行と増えていくのを見た時に、このJavaScriptの神話はとても落ち着かない気持ちにさせてくるのがわかりました。行数の限界をもっと押し上げておくのをお勧めします！ どこまで行けるのか試してみてください。コメントヘッダを使ってみたり、補助関数を作ってみましょう。でもそれらをすべてひとつのファイルに入れたままにしておいてください。これは自分自身で経験する価値があります！

1000行越えの`main.elm`一刀流でもいい…ってコト！？