Elm 0.19 の初期化方法 6 種類

2019 年 Elm をはじめる人が次に読むページです。
（いえ、どこから読んでも良いです）

この記事では Elm 0.19 のプログラム初期化の方法を紹介します。
（Elm コードの書き方は紹介しません）

## 出力は HTML と JS の２種類

HTML を出力（デフォルトは index.html）。

```
elm make src/Main.elm
```

ただしこの方法では CSS ファイルへのリンクやメタ情報を埋め込むことができないので、大抵は次のようにして JS を吐き出して HTML から呼び出します。

```
elm make src/Main.elm --output=elm.js
```

```html
<!DOCTYPE html>
<html>
<head>
    <script src="elm.js"></script>
    <title>Elm App</title>
</head>
<body>
    <script>
        Elm.Main.init();
    </script>
</body>
</html>
```

Node.js 環境ではこんな感じ。

```javascript
const { Elm } = require("./elm.js");
```

## 静的な HTML

main に直接 HTML を書きなぐれます。

```elm
module Main exposing(main)

main : Html msg
main = a [ href "https://elm-lang.org/" ] [ text "Elm" ] 
```

```javascript
Elm.Main.init({ node: document.getElementById("elm-node") });
```

## Browser.*

Elm 0.19 では main を Browser モジュールを使って書きます。0.18 以前では Html.program とか書いてありますが捨ててください。
詳しくは公式ガイドとパッケージのドキュメントを見てください。

- [The Elm Architecture](https://guide.elm-lang.org/architecture/) ([日本語](https://guide.elm-lang.jp/architecture/))
- https://package.elm-lang.org/packages/elm/browser/latest/Browser


### Browser.sandbox

HTTP などを使わない簡単なプログラムを作ります。

```elm
sandbox :
    { init : model
    , view : model -> Html msg
    , update : msg -> model -> model
    }
    -> Program () model msg
```

HTML から呼び出す場合にはノードに埋め込んで使います。

```javascript
Elm.Main.init({
  node: document.getElementById("elm-node")
});
```

### Browser.element

HTTP など副作用のある処理がある（ほとんど）場合はこちらを使います。

```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
```

HTML から呼び出す場合にはノードに埋め込んで使います。

```javascript
Elm.Main.init({
  node: document.getElementById("elm-node")
});
```

Flags や Ports の機能を使うと **Elm <=> JS 間でデータのやりとりができます**。

```javascript
var app = Elm.Main.init({
  node: document.getElementById("elm-node"),
  // フラグを渡す
  flags: { initializedAt : Date.now() },
});
// Elm から値を受け取る
app.ports.ping.subscribe(function (text) {
    // Elm に値を返す
    app.ports.pong.send(text);
});
```

### Browser.document

`element` とほとんど同じですがタイトルを動的に切り替えることができます。

```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

type alias Document msg =
    { title : String
    , body : List (Html msg)
    }
```

HTML から呼び出す場合は画面いっぱいに表示します。**埋め込みはできません**

```javascript
Elm.Main.init();
```

Flags と Port はもちろん使えます。

### Browser.application

最終進化形です。 **組み込みの SPA ルーティング機能が使えます**  。

```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
```

HTML から呼び出す場合は画面いっぱいに表示します。**埋め込みはできません**

```javascript
Elm.Main.init();
```

Flags と Port はもちろん使えます。

注意点としては、埋め込み（`element`）＋組み込みの SPA ルーティング機能を使うことはできません。理由と対策については[こちら](https://github.com/elm/browser/blob/1.0.0/notes/navigation-in-elements.md)を見てください。

## Platform.worker

画面なしのプログラムです。Node.js での使用が想定されています。

```elm
worker :
    { init : flags -> ( model, Cmd msg )
    , update : msg -> model -> ( model, Cmd msg )
    , subscriptions : model -> Sub msg
    }
    -> Program flags model msg
```

```javascript
const { Elm } = require("./elm.js");
const app = Elm.Main.init({
  flags: { initializedAt : Date.now() },
});
app.ports.pong.subscribe(function (text) {
    console.log(text);
});
app.ports.ping.send(text);
```

`worker` は elm-test などで使われています。
（追記）Node.js で使えるとはいえ、実用できるのはせいぜい小さなツール程度です。プロダクションのサーバーサイドなどガチ用途で使うのは絶対にやめてくださいね！

## まとめ

Elm でどんな初期化ができるのかをざっと紹介しました。
もりもりアプリを作っていきましょう。