Elm

Elm 0.18 入門(5) Subscriptionsによるイベント処理、関数合成

前回: Elm 0.18 入門(4) 時刻の取得とMaybe

前回はアプリケーション初期化時の現在時刻の取得を実装しました。
今回は現在時刻を毎秒自動更新するようにしてみましょう。

そもそも subscriptions とは

subscriptions は「予約」「購読」などの意味で、イベントに応じて自動的にアクションを発火するための仕組みです。
今回紹介する一定時間ごとのイベントの他にも(ボタン押下でないような)マウスクリックイベントやキーボード、WebSocketのリクエストなど様々なものがイベントとして指定できます。

また、 subscriptions 関数の入力値として model を指定することから推測できるように、モデルの状態に応じて反応を変化させることもできます。

subscriptions の実装

まずは subscriptions 関数を次のように更新します。

 subscriptions : Model -> Sub Msg
 subscriptions model =
-    Sub.none
+    let
+        updateCurrentDate t = 
+            Date.fromTime t |> UpdateDatetime
+    in
+        Time.every Time.second updateCurrentDate

初めて Time モジュールを使うので import を追加します。

import Time exposing (Time)

in の中から順に関数シグネチャを確認しましょう。

Time.every : Time -> (Time -> msg) -> Sub msg
Time.second : Time

Time.every は第一引数にインターバル時間を受け取り、そのインターバルで第二引数の関数を呼び出して、アクション(msg)を生成する Sub msg を出力します。

Time 型は実際は Float 型なので、例えば5秒ごとに処理をする subscriptions が作りたい場合は Time.every (5 * Time.second) ... とすれば良いです。
よりDSLっぽく書きたければ、 import Time exposing (Time, every, second) として every (5 * second) ... のように書いてもいいでしょう。

UpdateDatetime : Date -> Msg
Date.fromTime : Time -> Date

呼び出す関数には既に実装している時刻更新用のアクションを作る関数(コンストラクタ)を組み合わせます。ただ、こちらの関数は Date 型を引数にとるので、 Date.fromTime 関数で Time 型の値を変換する必要があります。
|> に関しては前回の記事を参考にして下さい。

さて、時刻表示が時計のように動いているでしょうか?

ココまでのコード : https://ellie-app.com/cDXLnLdsRa1/2

関数合成

さきほどの記述では、 let ... in ... を使って呼び出す関数部分をわざわざ定義していましたが、実際には必要ありません。 Time -> DateDate -> Msg を組み合わせて Time -> Msg の関数を作ればよいのです。これを関数合成と言います。

正にそのようなシグネチャを持つ中置演算子 >> があります。

(>>) : (a -> b) -> (b -> c) -> a -> c

http://package.elm-lang.org/packages/elm-lang/core/5.1.1/Basics#>>

関数合成を利用することで仮引数 t を用いた関数定義をしなくても、複数の関数そのものを合成して新しい関数を生成できるのでコードがスッキリします。

 subscriptions : Model -> Sub Msg
 subscriptions model =
-    let
-        updateCurrentDate t =
-            Date.fromTime t |> UpdateDatetime
-    in
-        Time.every Time.second updateCurrentDate
+    Time.every Time.second (Date.fromTime >> UpdateDatetime)

ただ、一般には関数合成というと逆の順番で合成をすることが多いです。

f : a -> b
g : b -> c
(<<) : (b -> c) -> (a -> b) -> a -> c
h = g << f

これは、通常の関数適用で考えると g (f x) となることから、 g << f (Haskellだと g . f )という並び方が自然になるためです。
読みやすい方を選んで使うようにしましょう。

ココまでのコード : https://ellie-app.com/cDXLnLdsRa1/3

まとめ

まずはシンプルな subscriptions の実装をしてみました。
次回は何を作ろうか決めてないですが、アプリケーションっぽいものを作れたらなと思います。