Elm Architecture + Effects （パート１）

## おさらい

Elmアーキテクチャについては、以下の記事で簡単に紹介しました。

- [最小構成ではじめるElm Architecture](http://qiita.com/chuck0523/items/610cbc5039b7f7db1eee)
- [Elm Architecture イントロダクション（テキストフィールド編）](http://qiita.com/chuck0523/items/a909b81522a97e50656c)

ViewからUpdateが発行されたらModelが変更され、Viewが再レンダリングされるという世界ですね。

今回は、ElmアーキテクチャにEffectを盛り込んでいきます。
<sub>
この記事は、[The Elm Architecture + Effects ](http://guide.elm-lang.org/architecture/effects/)に沿って進めていきます。

## Effectsとは

- Commands
- Subscriptions

Effectsとは上記の機能です。これらの機能を用いることで、Elmのコンポーネントが外部の世界とつながりをもつことができます。
SubscriptionsはElm0.17の目玉機能でもありますね。

[関数型リアクティブプログラミングにさよならを](http://qiita.com/chuck0523/items/28b07968a941d8d493d2)

### Commandsとは

何かしらのEffectを要求するものらしいです。
乱数を求めたり、HTTPリクエストを発行するのがCommandの役割だとか。実行結果は外部要因によって変化しえます。

### Subscriptionsとは

Subscriptionsとは、Geolocationの変化や、WebSocket経由でのメッセージ受信のようなイベントを待ち、それらが起こると更新処理が走るような仕組みです。

## アーキテクチャのスケルトンを拡張

Effectsを盛り込んだアーキテクチャスケルトン（骨組み）は、以下のようになります。

```hs
-- Model
type alias Model =
  { ...

  }

-- Update
type Msg = Submit | ...

update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
  ...

-- View

view : Model -> Html Msg
view model =
  ...


-- Subscriptions
subscriptions : Model -> Sub Msg
subscriptions model =
  ...

-- Init

init : (Model, Cmd Msg)
init =
  ...

```

Subscriptionsやinitが追加されたり、updateの結果にCmdというものが増えたのがわかります。

以下、詳しく見ていきます。

### update関数

update関数は、新しいModelだけでなく、実行したいコマンドも返すようになりました。これらのコマンドはすべて、Msgを生成しますが、Msgはupdate関数へと送られます。（Msgはアクションを識別する名前のようなものでした。）

### subscriptions関数

subscriptions関数では、現在のModelの変更を監視するためのイベントソースを宣言します。
Html MsgやCmd Msgのように、subscriptionsもupdate関数に渡すためのMsgを生成します。

### init

今回は、initは単なる初期Modelです。（Reduxで言うところのinitialState）
update関数のように、ModelとCommandsを提供します。ここでは、初期化のために値を渡したり、最初のHTTPリクエストを発行するような、初期化に必要なすべてのことを行います。

いまはまだこれらのことが分かっていなくても大丈夫です。実際の動きを見てみましょう。

# Rolls Dice

まずは、1~6の数値をランダムに生成するような”rolls dise”というアプリケーションを作成します。

ここでは、最小構成をつくるフェーズと、詳細を埋めていくフェーズの２つに分けて進めていきます。

## フェーズ１ 最小構成

まずはModelです。ModelはシンプルにInt型のデータを１つ持ちます。

```hs
-- Model
type alias Model =
  { dieFave : Int
  }
```

次にViewを書きます。

```hs
-- View
view : Model -> Html Msg
view model =
  div []
    [ h1 [] [ text (toString model.dieFace)]
    , button [ onClick Roll ] [ text "Roll" ]
    ]
```

- [h1 - Html](http://package.elm-lang.org/packages/evancz/elm-html/4.0.1/Html#h1)
- [onClick - Html.Events](http://package.elm-lang.org/packages/evancz/elm-html/4.0.1/Html-Events#onClick)
- [toString - Core](http://package.elm-lang.org/packages/elm-lang/core/1.0.0/Basics#toString)

次にUpdateを埋めていきます。

```hs
-- Update
type Msg = Roll

update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
  case msg of
    Roll ->
      (model, Cmd.none)
```

Elmアーキテクチャを学んだ時と異なり、update関数はModelだけでなくCmdも返しています。
ただし、今のところは、Cmd.none(なにもしない)をセットしておきます。

- [none - Platform.Cmd](http://package.elm-lang.org/packages/elm-lang/core/4.0.0/Platform-Cmd#none)

この部分が「ランダムな数値を取得する」に置き換えられるわけですね。


最後に、initをつくります。

```hs
init : (Model, Cmd Msg)
init =
  (Model 1, Cmd.none)
```


ちなみに、subscriptionsはまだ使わないので、以下のようにしておきます。

```hs
-- Subscriptions
subscriptions : Model -> Sub Msg
subscriptions model =
  Sub.none
```

- [none - Platform.Sub](http://package.elm-lang.org/packages/elm-lang/core/4.0.1/Platform-Sub#none)

なぜ使わないのに、subscrptionsを定義するかというと、いわばnot nullのようになっているからです。

最後に、ファイルの先頭にimportやスタート記述を書きます。
現時点でのファイルは、以下のようになっているはずです。

```hs
import Html exposing (..)
import Html.App as App
import Html.Events exposing (..)
import Random

main =
  App.program
    { init = init
    , view = view
    , update = update
    , subscriptions = subscriptions -- ←App.programはsubscriptionを要求する。
    }

-- Model
type alias Model =
  { dieFace : Int
  }

-- Update
type Msg = Roll

update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
  case msg of
    Roll ->
      (model, Cmd.none)

-- View
view : Model -> Html Msg
view model =
  div []
    [ h1 [] [ text (toString model.dieFace)]
    , button [ onClick Roll ] [ text "Roll" ]
    ]


-- Subscriptions
subscriptions : Model -> Sub Msg
subscriptions model =
  Sub.none

-- Init

init : (Model, Cmd Msg)
init =
  (Model 1, Cmd.none)

```

これでフェーズ１は終了です。
コンパイルが通るので、ブラウザで確認ができます。やりましたね！

## フェーズ２ 完成

乱数発生周りが大きく欠けている状態なので、そこを埋めます。

まずはupdateを追加します。

```hs
-- Update
type Msg
  = Roll
  | NewFace Int -- New!
```

NewFace関数が呼ばれると、Elmが乱数を渡してくれます。

関数部分も適用したupdateがこちら。

```hs
-- Update
type Msg
  = Roll
  | NewFace Int

update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
  case msg of
    Roll ->
      (model, Random.generate NewFace (Random.int 1 6))
    NewFace ->
      (Model newFace, Cmd.none)

```

いくつかの変更が行われていますね。

1. NewFaceメッセージ(Msg)を受け取ったパターンを追加しました。Modelを渡して、Commandは何も実行しません。
2. Rollに変更があります。Randomライブラリの持つ関数を２つ使っていますね。

- [Generators - Random](http://package.elm-lang.org/packages/elm-lang/core/4.0.1/Random)
- [int - Random](http://package.elm-lang.org/packages/elm-lang/core/4.0.1/Random#int)

大切なのは、Random.generateです。

```hs
Random.generate : (a -> msg) -> Random.Generator a -> Cmd msg
```

Random.generate関数は２つの引数を取ります。１つ目が乱数をタグ付けするための関数です。今回は、Intを取り、Msgを返すNewFace関数を渡しました。
そして、２つ目の引数は”generator”です。

- [generator - Random](http://package.elm-lang.org/packages/elm-lang/core/4.0.1/Random#Generator)

generatorはIntだけでなく、FloatやStringの値も生成できるようです。

```hs
Random.int : Int -> Int -> Random.Generator Int
```

これで、要求通りに機能するアプリケーションができました。

ここまでの学びは以下のとおりです。

- プログラムは少しずつ書きすすめること。シンプルなスケルトンからはじめて、徐々にタフな機能をつけていく。
- update関数は、いまや新しいModelとCommandを生成します。
- すぐにランダム値を取得することはできません。Commandを生成して、Elmに生成させるのです。


完成したコードは以下のとおりです。

```hs
import Html exposing (..)
import Html.App as Html
import Html.Events exposing (..)
import Random

main =
  Html.program
    { init = init
    , view = view
    , update = update
    , subscriptions = subscriptions
    }

-- Model
type alias Model =
  { dieFace : Int
  }

-- Update
type Msg
  = Roll
  | NewFace Int

update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
  case msg of
    Roll ->
      (model, Random.generate NewFace (Random.int 1 6))
    NewFace newFace ->
      (Model newFace, Cmd.none)

-- View
view : Model -> Html Msg
view model =
  div []
    [ h1 [] [ text (toString model.dieFace)]
    , button [ onClick Roll ] [ text "Roll" ]
    ]


-- Subscriptions
subscriptions : Model -> Sub Msg
subscriptions model =
  Sub.none

-- Init

init : (Model, Cmd Msg)
init =
  (Model 1, Cmd.none)

```

長くなってしまったので、ここでいったん切ります。
次回は、HTTP、時間ごとの処理、アニメーション、WebSocketsについて学んでいきます。

最後にもう１度、本家のリンクを貼っておきます。

http://guide.elm-lang.org/architecture/effects/

何か間違いなどがありましたら、コメントでお知らせください。