Cmd
「いきなり副作用を起こせって言われても困んど〜(こまんど〜)」
驚かれるかもしれませんが、なんとElmでは副作用を自分で起こすコードを書くことができません。
ここで言う副作用とは、Elm内で保持しているModelの内容をそのまま画面の表示内容に反映させる以外の何らかの作用のことを意味していて、例えばサーバーに対して処理を投げることであったり、画面をスクロールさせることであったり、あるいは現在時刻を取得することであったりします。
さて、ではElmを使うとそのような副作用をともなうプログラムを書くことができないということでしょうか?
サーバーにアクセスすることもできないなんてやっぱりElmは騙された人が使う机上の空論言語なんでしょうか?
いえ、そんなことはありません(反語)。
Elmには、副作用を自分で起こすコードを書く術は用意されていませんが、「こういうときにこういう副作用を起こしてその結果だけちょうだいね!」という設計書を書くことができます。
その設計書にしたがって、 Elmの実行環境が 実行時に副作用を起こしてくれます。
なんだか狐につままれたような気分ですよね?
「だったら最初から自分で副作用を起こせるようにしてくれればいいじゃん! なにが違うんだよ??」って思いますよね?
その答えは「事前に意図通りのコードになっているか判別しやすいから」です。
「意図通りのコードになっているか」を判別する方法の1つがテストですが、副作用を起こすコードをテストするためには特殊な処置が必要になります。
たとえば仮想の実行環境を用意し、副作用で得られる結果や影響をシミュレートする必要があります。
それに対して、「こういうときにこういう副作用を起こしてね」という設計書を使う方法であれば、その設計書をチェックすればいいだけですから、特に仮想環境も必要なくふつうのテストと同じように関数への入力値とそれに対する正しい出力値のペアを渡せばいいだけです。
ね、なんかそっちの方がいい気がしてきたでしょ?
さて、この「設計書」を実現するのが Cmd です。
実際の使い方は簡単です。
main 関数を作るときに使う program の型を見てみましょう。 program : { init : (model, Cmd msg), update : msg -> model -> (model, Cmd msg), subscriptions : model -> Sub msg, view : model -> Html msg } -> Program Never model msg
init と update にのみ Cmd msg という型があることから分かる通り、画面ロード時に呼ばれる関数 init として設計書を仕込むか、ユーザーの操作などによって画面の状態が変えるべきときに呼ばれる update に仕込むかしかありません。
init に設計書を仕込む
こちらは型が (model, Cmd msg) となっていることから分かるように、単純に画面初期ロード時の最初の状態と、初期ロード時に実行してほしい副作用に関する設計書をペアにして渡すだけです。
code:Elm
init : (Model, Cmd Msg)
init =
( initModel, initCmd )
initCmdが最初に実行される
updatに設計書を仕込む
code:Elm
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
NoOp ->
( model, Cmd.none )
DoSomething ->
( model, apiRequest )
...
apiRequestがCmd Msg
Elmの実行環境が渡されたCmd Msgを実行します