チェシャ猫の消滅定理

数学にプログラミング、素敵なもの何もかも。

We Are JavaScripters! @26th で Elm と Firebase の連携について話してきました

先日行われた We Are JavaScripters! @19th で Elm と JavaScript ライブラリの連携について発表してきました。

Elm の初心者向けの解説としてよく Msg, Model, update からなるアーキテクチャが挙げられていますが、今回の発表ではもう一歩だけ進んで、Cmd と Sub を使って Elm から JavaScript のライブラリを呼ぶ方法について解説しました。

サーバとしての JS ライブラリ

他の AltJS では JavaScript を呼び出す際、ソースコードの内部に埋め込む形になるのが普通です。

例えば HaskellJavaScriptコンパイルする GHCJS の場合、JSaddle という DSL を利用して次のように呼び出すことになります。

store :: Int -> IO ()
store n = runJSaddle () $ do
    ref <- jsg "firebase" ^. js0 "database" ^. js1 "ref" (val "/counter")
    ref ^. js1 "set" (val n)
    return ()

Firebase SDK を呼び出して Realtime DB に値をセットする部分です。JavaScript 側の関数を文字列で指定することで、直接 Haskellソースコード内に IO アクションとして JS の呼び出しが定義されていることがわかります。この場合、Haskell とは別に JavaScript 側を自分で実装する必要はありません。

一方、Elm で同様の呼び出しを実装する場合、「Port」「Command」「JavaScript 側での subscribe」という 3 つの部分に分割されます。

まず、JavaScript 側へのインタフェースとなる Port を次のように定義します。

port module RealtimeCounter.Port exposing (store)

import Json.Encode as E

port store : E.Value -> Cmd msg

JSON を受け取って、Cmd を発生させる関数 store が定義されています。port に指定された関数は型シグネチャだけが存在し、中身は記述しません。

次に、実際に update 関数の中でこの Cmd を発生させるために、Elm の Int 値を JSON エンコードしてこの port に流し込む関数を定義します。

import Json.Encode as E

storeCount : Int -> Cmd msg
storeCount =
    store << E.int

最後に、Elm 側から発生した Cmd を受け取るための JavaScript を実装します。

const { Elm } = require('./Main.elm');
const app = Elm.Main.init({
    node: document.getElementById('app')
});

app.ports.store.subscribe((count) => {
    firebase.database().ref('count').set(count);
});

上に挙げたコールバックによる実装を見ても分かる通り、JavaScript 側はあたかも Web サーバのコントローラのように subscribe で待ち受けており、Elm から Cmd によって JSON が渡されると実際に Realtime DB に値をセットします。

逆に、JavaScript 側から Elm 側に値を戻す必要がある場合には、JavaScript 側で send 関数を使用すると Elm 側からは Sub となって観測されます。具体的には下記のサンプルレポジトリを参照してください。

github.com

なお、実際に JS 連携する部分をコーディングする際には、boiyaa さんが書かれている詳しい記事も参考になると思います。

boiyaa さんの記事は Elm v1.18 時点で書かれているので起動時に Html.programWithFlags を使用していますが、Elm v1.19 ではここに相当する関数は Browser.element に変更になっています。

動作サンプル

実際に触って動かせるデモもデプロイしておきました。先に挙げたソースコードと合わせて、よかったら参考にしてみてください。

f:id:y_taka_23:20181124221326p:plain デモページ

プレゼンの際にお見せしたデモは単にテキストを置いただけの画面でしたが、公開版はちょっと凝った CSS を付けてみました。カウンタ自体の Elm 側のロジックは非常にシンプルで行数も少ないので、GitHub からは Elm ではなく CSS のレポジトリ扱いされてしまっていますが、それはまた別の話。