はじめに
Elm は、JavaScript に似た構文を持つ Web アプリケーションを作るための言語です。
本記事では HTML や JavaScript の例を交えながら、Elm についてフワッとわかった気になってもらうことを趣旨としてます。
tl;dr
処理の流れ
- init 関数で Model の最初の値を定めて、view 関数に渡される
- view 関数で 渡された Model によって Web ページが表示される
- ユーザーの操作があると Msg が通知され、update 関数に渡される
- update 関数で Msg によって処理が分岐し、 Model が更新されて view 関数に渡される
- 1.へ戻る
コーディングの流れ
- view 関数で Web ページを作る
- view 関数から**表示を変化させたい値(Web ページの状態)**を抜き出して Model を作る
- view 関数で Model から Web ページを作るようにする
- init 関数で Model の最初の値を作る
- view 関数からユーザーが操作した、その次に起きることを抜き出して Msg を作る
- view 関数で、HTML のイベントが起きたときに、Msg の値が通知されるようにする
- update 関数で、受け取った Msg によって処理が分岐し Model を更新する処理を作る
前提知識
- HTML と JavaScript を触ったことがある
- 変数や関数、配列、オブジェクトについて知っている
注意
JavaScript に似た構文を持つ Elm ですが、異なる部分もあるので注意が必要です。1 2
特に、関数定義と関数呼び出しでは、括弧とカンマでなく、スペースを使います。
また、return がないことも特徴です。
function sum (x, y) { // 関数定義
return x + y;
}
sum(1, 2); // 関数呼び出し
function puls1 (x) {
return x + 1;
}
plus1(sum(1, 2))
sum x y = x + y; -- 関数定義
sum 1 2 -- 関数呼び出し
plus1 x = x + 1;
plus1 (sum 1 2)
また、 Elm には JavaScript にない、型注釈というものがありますが、いつかまた説明しましょう。
sum : Int -> Int -> Int -- 型注釈
sum x y = x + y; -- 関数定義
view 関数
まずは、以下のリンクを開いて、 -- VIEW 以下を見てみましょう
https://ellie-app.com/9WqRmw9Hkf9a1
-- VIEW
view : Model -> Html Msg
view model =
div []
[ button [] [ text "+" ]
, div [] [ text "15" ]
, button [] [ text "-" ]
]
このコードは、カウンターのボタンとカウンターの値を表示するだけです。
試しにボタンを触っても何も起きません。
view 関数で Web ページの見かけを作る
通常、Web ページに表示されるものは、HTML で記述されます。
例えば、ページのタイトル、次のページへのリンク、送信ボタン、など。
しかし、Elm では HTML の代わりに view 関数で Web ページを作ります。
view 関数に見出しを追加する
view 関数の中の div 関数や button 関数と、HTML の div タグや button タグを見比べると、よく似ていることがわかります。
<div>
<button>-</button>
<div>15</div>
<button>+</button>
</div>
div []
[ button [] [ text "+" ]
, div [] [ text "15" ]
, button [] [ text "-" ]
]
試しに h1 関数で見出しを追加してみましょう。
-- VIEW
view : Model -> Html Msg
view model =
div []
[ h1 [] [ text "Hello World" ]
, button [] [ text "+" ]
, div [] [ text "15" ]
, button [] [ text "-" ]
]
HTML の属性
HTML の要素の関数以外にも、HTML の属性の関数もあります。3
例えば class 関数や、id 関数、style 関数などです。
<h1 class="title" id="firstTitle" style="color:red">Hello, World</h1>
h1 [ class "title", id "firstTitle", style "color" "red" ] [ text "Hello World" ]
Model
今度は、 -- MODEL 以下を見てみましょう
https://ellie-app.com/9WqRmw9Hkf9a1
-- MODEL
type alias Model =
{ count : Int }
init : Model
init =
{ count = 0 }
Model で、Web ページの状態を作る
view 関数で、Web ページの見かけを作りましたが、このままではカウンターに表示されるのは常に同じ "15" という定数です。
この定数を変数にして、値を更新できるようにするために、Elm では Model を利用します。
type alias Model =
{ count : Int }
Model は、レコードという JavaScript のオブジェクトのようなものです。4
この count というフィールドが、カウンターの値となります。
view 関数で Model から Web ページを作る
view 関数では、model という値を受け取っていました。
そして model.count でカウンターの値を取り出すことができます。
-- VIEW
view : Model -> Html Msg
view model =
div []
[ button [] [ text "+" ]
, div [] [ text "15" ]
, button [] [ text "-" ]
]
view 関数の、text "15"
の部分を、model.count に変えてみましょう。
-- VIEW
view : Model -> Html Msg
view model =
div []
[ button [] [ text "+" ]
, div [] [ text (String.fromInt model.count) ]
, button [] [ text "-" ]
]
ちなみに String.fromInt 関数 は、数値 (Int) を 文字列 (String) に変換する関数です。5
init 関数で、Web ページの最初の状態を作る
Web ページにアクセスしたときの、Model の最初の値は、init 関数で決めることができます。
-- MODEL
type alias Model =
{ count : Int }
init : Model
init =
{ count = 0 }
試しに、最初のカウントを10からに変えてみましょう
-- MODEL
type alias Model =
{ count : Int }
init : Model
init =
{ count = 10 }
まとめ
- view 関数で Web ページを作る
- view 関数から**表示を変化させたい値(Web ページの状態)**を抜き出して Model を作る
- view 関数で Model から Web ページを作るようにする
- init 関数で Model の最初の値を作る
Msg と update 関数
次に、 -- UPDATE 以下を見てみましょう
https://ellie-app.com/9WqRVH9bQjma1
-- UPDATE
type Msg
= Increment
| Decrement
update : Msg -> Model -> Model
update msg model =
model
Msg で「ユーザーが操作した、その次に起きること」を作る
画面のフォームに対して、ユーザーから何か操作があったとき、「その次に起きること」を表現してみましょう。
Msg は「ユーザーが操作した、その次に起きること」を表すものです。
今回は
-
- ボタンがクリックされたとき、「カウントが増える」
-
- ボタンがクリックされたとき、「カウントが減る」
の2つです。
-- UPDATE
type Msg
= Increment
| Decrement
Increment は「カウントが増える」ということを表し、Decrement「カウントが減る」ということを表しています。
さて HTML のイベントが起きたときに、Msg の値が通知されるようにしましょう。
-- VIEW
view : Model -> Html Msg
view model =
div []
[ button [] [ text "+" ]
, div [] [ text (String.fromInt model.count) ]
, button [] [ text "-" ]
]
-- VIEW
view : Model -> Html Msg
view model =
div []
[ button [ onClick Increment ] [ text "+" ]
, div [] [ text (String.fromInt model.count) ]
, button [ onClick Decrement ] [ text "-" ]
]
onClick 関数は、HTML のイベント属性である onclick を表しています。
そして、ここで通知された Msg の値が、次の update 関数に渡されます。
update 関数で「実際に何が起きるか」を作る
update 関数は、受け取った Msg に連動して「実際に何が起きるか」を表すものです。
具体的には、update 関数は、受け取った Msg によって分岐して Model を更新する関数です。
それでは
- 受け取った msg が Increment ならば model.count を1増やす
- 受け取った msg が Decrement ならば model.count を1減らす
この2つの処理を書いてみましょう。
-- UPDATE
type Msg
= Increment
| Decrement
update : Msg -> Model -> Model
update msg model =
case msg of
Increment ->
{ model | count = model.count + 1 }
Decrement ->
{ model | count = model.count - 1 }
case msg of
は JavaScript の switch 文のようなものです。6
また、{ xxx | ~~ }
はレコードを更新する構文です。
例えば、{ model | count = model.count + 1}
は、 model.count + 1 を新しい model.count としたレコードを作る、という意味です。
ここで作られた新しい Model が view 関数に渡り、Web の表示が更新されます。
まとめ
おめでとうございます!
ついに、カウンターが動くようになりました。
https://ellie-app.com/9WqWzcqp3nra1
ここまでの手順をまとめてみましょう。
- view 関数からユーザーが操作した、その次に起きることを抜き出して Msg を作る
- view 関数で、HTML のイベントが起きたときに、Msg の値が通知されるようにする
- update 関数で、受け取った Msg によって処理が分岐し Model を更新する処理を作る
The Elm Architecutre
ここまでの一連の流れがつかめたでしょうか?
- init 関数で Model の最初の値を定めて、view 関数に渡される
- view 関数で 渡された Model によって Web ページが表示される
- ユーザーの操作があると Msg が通知され、update 関数に渡される
- update 関数で Msg によって処理が分岐し、 Model が更新されて view 関数に渡される
- 1.へ戻る
このようなサイクルを繰り返して、画面が動くようになりました!
これが The Elm Architecutre です。
そんなに難しくないでしょう?
全体のまとめ
処理の流れ
- init 関数で Model の最初の値を定めて、view 関数に渡される
- view 関数で 渡された Model によって Web ページが表示される
- ユーザーの操作があると Msg が通知され、update 関数に渡される
- update 関数で Msg によって処理が分岐し、 Model が更新されて view 関数に渡される
- 1.へ戻る
コーディングの流れ
- view 関数で Web ページを作る
- view 関数から**表示を変化させたい値(Web ページの状態)**を抜き出して Model を作る
- view 関数で Model から Web ページを作るようにする
- init 関数で Model の最初の値を作る
- view 関数からユーザーが操作した、その次に起きることを抜き出して Msg を作る
- view 関数で、HTML のイベントが起きたときに、Msg の値が通知されるようにする
- update 関数で、受け取った Msg によって処理が分岐し Model を更新する処理を作る
完成品
-
Elm Syntax/Records
また、レコードの中の変数はフィールドと呼びます。 ↩ -
package: elm/core/fromInt
また後で説明しますが Elm ではこのように、値の変換をキッチリとしなければなりません。 ↩ -
Elm Syntax/Conditonals
msg が Increment のときと、Decrement のときで分岐することを表しています。 ↩