SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

Webアプリケーション開発技術の新潮流スタディーズ

関数型リアクティブプログラミング言語Elmに学ぶ フロントエンド開発の新しい形 【後編】

Webアプリケーション開発技術の新潮流スタディーズ 第5回


  • X ポスト
  • このエントリーをはてなブックマークに追加

ダウンロード samples.zip (8.2 KB)

Elm Architectureとその特徴

いよいよアプリケーションを作りたいところですが、何か指針がないと、秩序のあるアプリケーションをスムーズに構築することはできません。

そこで、Elmでは「Elm Architecture」と呼ばれる設計手法に従うことが強く推奨されています。 これは、他のJavaScriptフレームワークにおけるMVCやFlux[1]といった概念に相当します。 さっそく中身を見ていきましょう。

[1]: Fluxは、React.jsを効果的に使うためにFacebookによって提唱されたアーキテクチャです。

一方向のデータフロー

従来のアプリケーションでは各UIコンポーネントに状態を持ち、それを必要に応じて取り出して使うという方法を採っていましたが、規模が大きくなるとすぐにどこで何をしているのか分からなくなってしまいます。

Elm Architectureではデータフローを一方向に保ちます。 具体的には、ユーザアクションやサーバイベントを起点にして、アプリケーション内部に保持するデータを更新し、最終的にそのデータが画面描画のロジックに流れていきます。

データの流れを一方向に保つ
データの流れを一方向に保つ

Model・Update・View

Elm Architectureでは、プログラムを大きくModelUpdateViewという3つのセクションに分けて記述します。以下、モデルやビューといった用語は良く知られたMVCアーキテクチャの用語を流用しますので、説明不足を感じられましたらご容赦ください。また、クリックなどのイベントをここではアクションと呼んでいます。

Elm Architectureの3つのセクション
セクション名 役割
Model プログラム中で使うモデルを定義する
Update アクションを受け取り、モデルの更新を行う
View 画面描画とアクションの発行を行う

最も参考になるのは、Elmで書かれたTodoアプリでしょう[2]。 全体が1枚のファイルに書かれているため、上から下まで目を通すだけで雰囲気がつかめると思います。

[2]: Webアプリのフレームワークを比較する際には、同じTodoアプリを異なるフレームワークで作成したTodoMVCが参考になります。上記のTodoアプリはそのうちの1つです。

Elm Architectureを使った実装の基本パターン

次に、Elm Architectureを使ってアプリケーションを実装するときの基本パターンを見てきます。 ただし、「Elmで書かれたTodoアプリ」はここでの説明には大きすぎるため、もう少し小さいサンプルを用意しました。

ステップ1:Model・Update・Viewを定義する

次のサンプルは「ボタンを押した回数をボタン自身に表示する」というものです。

import Html exposing (..)
import Html.Attributes exposing (style)
import Html.Events exposing (onClick)

{- TODO: あとでここを埋めます -}

-- MODEL

type alias Model =
  { count : Int
  }

init : Model
init =
  { count = 0
  }

-- UPDATE

type Action
  = NoOp
  | Increment

update : Action -> Model -> Model
update action model =
  case action of
    NoOp -> model
    Increment ->
      { model |
        count <- model.count + 1
      }

-- VIEW

view : Signal.Address Action -> Model -> Html
view address model =
  button
    [ onClick address Increment
    , style [("width", "100px"), ("height", "100px"), ("font-size", "large")]
    ]
    [ text (toString model.count) ]

【コードの解説】

  • MODELでは、このアプリケーションで扱う全ての状態の型と初期値を定義しています。
  • UPDATEでは、このアプリケーションで扱う全アクションと、それぞれのアクションに対して状態を更新する関数を定義しています。更新といっても、Elmの変数は全てImmutableなので、古い値を受け取って新しい値を返す関数になっています。
  • VIEWでは、状態を入力としてHTMLを生成する関数を定義しています。ここでは、実際にDOMオブジェクトを生成しているわけではなく、DOMに見立てた仮想的なオブジェクト(Virtual DOM。後述のコラムを参照)を生成しています。VIEWで生成するHTML要素は全て要素名 [属性] [子要素]という関数になっているため、直感的に記述していくことができるでしょう。

ところで、VIEWセクションにaddressという変数が登場しています。 これは、メールアドレスと同じように、アクションを発行する際の「あて先」を表しています。 view関数の第1引数であるSignal.Address Actionという型は、「Action型を受け付けるAddress」を表しています。

アクションは要素の属性値として記述します。 onClick address Incrementという記述は、クリック時にIncrementというアクションをaddressに対して発行することを意味しています。 イベントやその他の属性の定義についてはevancz/elm-htmlを参照してください。

ステップ2:Signalを使って動作させる

さて、主要な関数は定義できましたが、mainすらないこのサンプルは、まだ動きません。 {- TODO: ... -}になっている部分を埋めていきましょう。

actions : Signal.Mailbox Action
actions =
  Signal.mailbox NoOp

state : Signal Model
state =
  Signal.foldp update init actions.signal

main : Signal Html
main =
  Signal.map (view actions.address) state

【コードの解説】

  • actionsはメールの受信箱です。先ほど説明したAddress宛てに発行されたActionはここに到達するわけです。ここに届いたActionは、actions.signalでSignalとして取り出すことができます。ここでは、初期値としてNoOpを設定しています。
  • stateは、常に最新の状態を持ったSignalです。actions.signalを入力、initを初期値として、Signal.foldp関数を使ってを常に最新の状態に更新していきます。
  • mainでは、Signal ModelからSignal Htmlへの変換を行い、常に最新の状態であるstateから最新の画面を生み出しています。

以上で、晴れてカウンタが動くようになりました。 ボタン1個しかない割にはたくさんのコードを書き連ねた感がありますが、この作業が必要になるのは最初の1回だけです。 以降は、機能を追加するときにModel・ Update・Viewの必要なところへコードを足すだけで済みます。

ステップ3:必要に応じて共通化する

ステップ2のコードをよく見ると、アプリケーション固有の情報がどこにも書かれていないことに気づくでしょう。 つまり、この部分は別の画面にも使い回すことができます。

実際にこの共通化を行っている「start-app」というライブラリがあるので、これを使って書いてもよいでしょう。 {- TODO: ... -}の部分を次のように書き直しても同じように動きます。

import StartApp.Simple as StartApp

main =
  StartApp.start { model = init, view = view, update = update }

この方法は手軽ですが、これではまかない切れないパターンも出てきます。そのときにはあきらめて、自分で書きましょう。 プロジェクト固有のパターンを作るなどしてもよいと思います。

基本パターンのまとめ

ここまでを図式にすると次のようになります。

基本パターンのイメージ
基本パターンのイメージ

Elm Architectureに従うと、全体で必要なSignalはたった3つです。

ElmとVirtual DOM

もしかすると、VIEWのロジックに違和感を覚えた方がいらっしゃるかもしれません。 コードを見る限りでは、何かアクションがあるたびにHTMLを上から下まで再描画しているように見えます。 DOMの描画コストが高いことを知っている方であれば、有無を言わさず避けたいコードでしょう。

しかし、ElmではVirtual DOMという仕組みを使うことによって、高速な描画を実現しています。

簡単に説明しましょう。 Virtual DOMとは、DOMを模した構造体です。 VIEW部分でイベントのたびに生成されるのは、この偽物のDOM(Virtual DOM)です。 ポイントは、Virtual DOMの生成コストが、本物のDOMに比べるとかなり低いことです。そのため、Virtual DOMであれば画面全体を作り直しても大したコストになりません。 そして、本物のDOMには、新旧のVirtual DOMから抽出された差分のみを反映します。 これが、高速な描画を実現するVirtual DOMの仕組みです。

Virtual DOMの仕組み
Virtual DOMの仕組み

Virtual DOMはReact.jsによって発明されたもので、そちらにも同じ説明があります。

しかし、Elmの場合には続きがあります。 Elmの言語特性である「純粋性」と「不変性」が、さらに高速な描画を可能にするのです。

Elmの純粋性と不変性がさらに高速な描画を可能にする
Elmの純粋性と不変性がさらに高速な描画を可能にする

状態に依存しない純粋な関数であれば、同じモデルが生成するビューは常に同じです。 つまり、モデル同士を比較してしまえばVirtual DOMすら作る必要がありません。 また、不変なデータ構造を持つことで、新旧のモデルの比較も容易になっています。

ElmのVirtual DOMに関しては、Elm公式のアナウンス「Blazing Fast HTML邦訳: 爆速HTML – Elmでの仮想DOM)」に詳しい説明があります。ぜひ参照してみてください。

次のページ
Elm Architectureの応用 ⑴:コンポーネントを作る

修正履歴

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
Webアプリケーション開発技術の新潮流スタディーズ連載記事一覧

もっと読む

この記事の著者

鳥居 陽介(株式会社ワークスアプリケーションズ)(トリイ ヨウスケ)

株式会社ワークスアプリケーションズ所属。イケてるアプリケーションを死ぬほど楽に作るために研究を続ける日々。社内での立ち位置は「フロントエンドのナウい人」。最近エバンジェリストという肩書きが付いた。趣味は作曲とスノーボード。 Blog: http://jinjor-labo.hatenablog.com/ ...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/8986 2017/03/02 16:39

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング