Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

Elm Architecture の Model-View-Updateについて

More than 1 year has passed since last update.

Elm開発環境について
Elm Architecture の Model-View-Updateについて

(2019/01/30 修正) 0.19対応 toString を String.fromInt で置き換えました。

 Elmは純粋な関数型言語であり、Webアプリ専用の言語でもあります。両者は相反するように思えますが、HaskellでもYesodを使ったWebアプリを作成できますので...。要するにHaskellは純粋でないダーティな部分はIOモナドで隔離します。同じようにElmではElm Runtimeにダーティな部分を任せて、自身の純粋さを保つようにしているようです。

 ElmはWebアプリのArchitecture をModel-View-Updateという形でパターン化しています。前回取り上げたソースコードで説明していきます。公式サイトにあるボタン表示のプログラムを微修正したものです。
https://guide.elm-lang.org/install.html

1.View Only

 その前に以下の簡略化されたソースコードを見てください。これは35という数字をブラウザに表示してくれるプログラムです。

MyButtons.elm(シンプル版)
module MyButtons exposing (..)

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

main = view "no model"
view model =
    div [] [ text (String.fromInt 35) ]

 MyButtons.elmはコンパイルされてelm.jsになります。以下のようにindex.htmlでelm.jsは読み込まれます。そしてElm.MyButtons.embed()でMyButtons moduleのmain関数がこのアプリのエントリーポイントとして使われるようになります。つまり関数 view "no model" が実行されます。

index.htmlのコア部分
        <div id="elm-area"></div>
        <script src="elm.js"></script>
        <script>
            Elm.MyButtons.embed(document.getElementById("elm-area"));
        </script>

 view関数は引数としてmodelを取りますが、右辺では使われていないのでダミーで十分です。結果的にHTML(DOM)を生成します。div [] [ text (String.fromInt 35) ] はdivタグを表します。最初のリスト[]は属性のリストで、2番目のリスト[ text (String.fromInt 35) ] は子要素のリストです。つまり <div>35</div>となり、index.htmlの中で以下のように展開されます。

<div id="elm-area"><div>35</div></div>

ブラウザにはこの35という数字が表示されるだけです

2.Model-View-Update

 今度はボタンを設置して、表示する数字を増減させることができるようにします。前回と同じソースコードを以下に示します。

MyButtons.elm
module MyButtons exposing (..)

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

main =
  Html.beginnerProgram { model = model, view = view, update = update }

-- MODEL
type alias Model = Int

model : Model
model =
  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 "+" ]
    ]

 このプログラムは、内部的にカウンターを持っていて、インクリメントボタンとディクリメントボタンの2ボタンを表示し、間に現在値を表示しています。インクリメントボタンをクリックするとカウントアップした値が表示され、ディクリメントでカウントダウンした値が表示されます。特にブラウザをリフレッシュすることなしに、ブラウザ上の値は動的に変わっていきます。

 このモデルは以下のような流れになります。

 ユーザINPUTが発生 → modelを更新 → viewに更新を反映 → ユーザINPUTが発生 → ....

 これはElm RuntimeがユーザINPUTイベントの管理やHTML表示を司っているので、以下のように書くのがもっと正確です。

           (MSG)                        (Html)
Elm Runtime → update → model → view → Elm Runtime 

 上のようなモデルは以下のmain関数で定義されています。繰り返しになりますが、内部カウンター(model)をマウスクリックでupdateし、その結果をviewに反映させます。ユーザのINPUTをハンドリングし、modelをupdateし、そのupdateをviewに反映させます。これを繰り返すのがELM Architecture の Model-View-Update Loopです。

main =
  Html.beginnerProgram { model = model, view = view, update = update }

 Html.beginnerProgram()はModel-View-Update Loopを実現化します。マウスでボタンが押されると(ユーザのINPUTがあると)、Elm RuntimeはMSG付きのイベントを発生させupdate関数を呼びます。update関数はMSGに従ってmodelを更新します。更新されたmodelでviewが呼ばれ、新しいHtmlが生成され、Elm Runtimeに渡され実際にブラウザで表示されます。つまりイベントとかDOM表示とかは全てElm Runtimeが下請けします。Virtual DOMが使われていますが、Elm Runtimeは高速に描画してくれるらしいです。

 ちなみにElm Runtimeはコンパイラが生成してくれるJavaScriptコードです。ソースコードに従ってイベントハンドラーを設定してくれたり、HTTP RequestやDON updateの効率的なスケジューリングを行ってくれたりします。

 それと指摘しておきたいのがmodelですが、これはElmのデータですので直接書き換えることはできません。一部分を変更しながらコピーすることで、書き換えの代替としています。Elmは純粋ですから。

3.Type

 Haskellは型が強い言語です。コンパイラの型チェックがとても厳しくて、逆に型チェックが通れば、大方デバッグ無しで動くともいわれています。おなじようにElmにも型があります。上のプログラムで少し説明を加えます。

 以下はModelという方を定義しています。Int型の別名として定義されているのでaliasが付いています。

type alias Model = Int

 以下はMsg型を定義しています。Msg型はIncrement または Decrement のことを指します。

type Msg = Increment | Decrement

 以下は関数updateの型です。Msg型とModel型の引数を取り、Model型の値を返します。

update : Msg -> Model -> Model

 以下は関数viewの型です。Model型の引数を取り、Html Msg型の値を返します。Html Msg型にはtype variableのMsgが含まれています。Htmlは引数を必要とする方で、Html Msgではじめて一つの型になります。一般的にはHtml a で a
がtype variableで任意の型が入ります。この場合はMsgがtype variableの値です。

view : Model -> Html Msg

 今回は以上ですが、Elmの話はまだ続きます

sand
Haskell、Elm、Elixir、Phoenixなどが好きな言語です。 確率統計を勉強中。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away