Elmは自分でできない汚れ仕事をElm Realtimeに下請けさせますが、その時のインターフェースとしてCmd/Subを使います。Subのexampleとして公式サイトに載っているWebSocketのプログラムを試しましたのでその記録です。
https://guide.elm-lang.org/architecture/effects/web_sockets.html
1.開発環境
最後にリストしますが、index.htmlとMyWebSocket.elmのファイルを用意します。開発の流れは以下の記事に従います。
Elm開発環境について - Qiita
以下のコマンドを実行します。
mkdir websocket
elm-package install elm-lang/websocket
index.htmlとMyWebSocket.elmをwebsocketディレクトリにおいてコンパイルします。elm-reactorを実行するとブラウザからアクセスできます。
elm-make MyWebSocket.elm --output elm.js
elm-reactor -a=www.xxxxx.jp -p=3030
2.index.html
コンパイル結果のelm.jsを読み込んで、そこからimportしたMyWebSocketモジュールを使って、Elm.MyWebSocket.embedでアプリを走らせています。
<!doctype html>
<html>
<head>
</head>
<body>
<div id="elm-area"></div>
<script src="elm.js"></script>
<script>
Elm.MyWebSocket.embed(document.getElementById("elm-area"));
</script>
</body>
</html>
3.MyWebSocket.elm
Elmプログラムの全てですが、大変シンプルなことに驚きます。以下に多少の説明を加えたいと思います。
module MyWebSocket exposing (..)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (..)
import WebSocket
main =
Html.program
{ init = init
, view = view
, update = update
, subscriptions = subscriptions
}
-- MODEL
type alias Model =
{ input : String
, messages : List String
}
init : (Model, Cmd Msg)
init = (Model "" [], Cmd.none)
-- UPDATE
type Msg
= Input String
| Send
| NewMessage String
update : Msg -> Model -> (Model, Cmd Msg)
update msg {input, messages} =
case msg of
Input newInput ->
(Model newInput messages, Cmd.none)
Send ->
(Model "" messages, WebSocket.send "ws://echo.websocket.org" input)
NewMessage str ->
(Model input (str :: messages), Cmd.none)
-- SUBSCRIPTIONS
subscriptions : Model -> Sub Msg
subscriptions model =
WebSocket.listen "ws://echo.websocket.org" NewMessage
-- VIEW
view : Model -> Html Msg
view model =
div []
[ div [] (List.map viewMessage model.messages)
, input [onInput Input] []
, button [onClick Send] [text "Send"]
]
viewMessage : String -> Html msg
viewMessage msg =
div [] [ text msg ]
index.htmlで使われるのでプログラムのモジュール名を宣言しておく必要があります。
module MyWebSocket exposing (..)
まずWebSocketの関連部分は以下のとおりです。これはelm-lang/websocketモジュールで提供される機能です。
WebSocket.send "ws://echo.websocket.org" input
WebSocket.listen "ws://echo.websocket.org" NewMessage
ws プロトコルは、WebSocket サーバとリソース名を識別する。exampleですので、ここではecho.websocket.orgという既存のエコーサーバが使われています。ちなみに通信がTLS により保護されることが必要なときはwssが使われます。
https://triple-underscore.github.io/RFC6455-ja.html#section-11.1.1
それぞれの型を以下に挙げると、使われ方もはっきりすると思われます。WebSocket.send はupdateの返り値のCmd部分に使われます。WebSocket.listenはsubscriptionsの定義に使われます。
WebSocket.send : String -> String -> Cmd msg
WebSocket.listen : String -> (String -> msg) -> Sub msg
subscriptions関数はイベントのリスナーのようなものを設置するためのものです。今回はWebSocketサーバ(echo.websocket.org)からの通信をリッスンします。通信を受け取るとNewMessage strという型でupdate関数が呼ばれます。NewMessageはlisten関数の第2引数で、strが通信内容ですね。
subscriptions : Model -> Sub Msg
subscriptions model =
WebSocket.listen "ws://echo.websocket.org" NewMessage
以上ですが、WebSocket モジュールがうまくCmd/Subのアーキテクチャにはまって、全体的にシンプルなプログラムになっていると思います。