elmでやってみるシリーズ9: JavaScript連携(JSのevalを呼ぶ)
Elmでやってみるシリーズ9: JavaScript連携(JSのevalを呼ぶ)
Elmではportというものを使用することで、ElmからJSの機能を呼ぶことができます(→Ports: Communicate with JS)。
portは名前がJS側に公開されるSignalです。
portには入力portと出力portがあります。
- 入力port: JS→Elm
- 出力port: Elm→JS
JS側ではJSの機能は何でも使えるのですが、以下ではevalを呼んでみました。
以下は画面キャプチャ。
コードは以下のとおり。
module PortTest where import Graphics.Input (button, input, Input) import Graphics.Input.Field as F inp : Input F.Content inp = input F.noContent btnInp : Input String btnInp = input "S" fld fldCont = F.field F.defaultStyle inp.handle id "JSの式を入力して下さい" fldCont btn fldCont = button btnInp.handle fldCont.string "Eval" port evalIn : Signal String port evalOut : Signal String port evalOut = btnInp.signal main : Signal Element main = let disp cont bname = flow down [fld cont, btn cont, plainText bname] in disp <~ inp.signal ~ evalIn
上記を呼び出すHTMLは以下。
<!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <title>Call JS from Elm</title> <script type="text/javascript" src="http://elm-lang.org/elm-runtime.js"></script> <script type="text/javascript" src="build/PortTest.js"></script> </head> <body> <div id="portTest" style="width:50%; height:400px;" ></div> </body> <script type="text/javascript"> var div = document.getElementById('portTest'); // embed our Elm program in that <div> elmModule = Elm.embed(Elm.PortTest, div, {"evalIn": "initial"}); elmModule.ports.evalOut.subscribe(jsEval); function jsEval(exp) { elmModule.ports.evalIn.send(eval(exp).toString()); } </script> </html>
実行画面(操作可能)
全画面はこちらから。
気づいたこと・解説
- JSからElmに渡される値の型は厳しくチェックされる。自動的にtoString()を呼んでくれたりはしない。
- portは「値」ベースの情報交換である。直接相互の関数を呼んだりはできない。
- Fregeと比べると興味深い。JSのpureな関数があってもそれをElmから直接を呼ぶことはできない(今のところ)
- 他のaltJSに比べれば、一手間かかるわけだが、evalは万能インターフェース(文字列限定)
- Elmにおいて煩雑に思われるJsonからの値のとりだしはJS側でやるという手もあるかもね。
- シグナル間の依存関係は以下のとおり。
evalOut:Signal String ^ "Eval"Button btnInp:Input String / btn:Element ==================================> handle:Handle String /btnInp.signal signal:Signal String ----------/ ^ :inp.signal.string :(fldCont = inp.signal; JS Exp Field inp:Input F.Content : btnInp.signal = Signal (fldCont.string) fld:Element ========> handle:Handle F.Content / signal:Signal F.Content ../ Result Text evalIn:Singal String ------> plainText bname:Element (凡例) signalA -------> signalB signalBはsignalAを参照している signalAの値が変更されるとsignalBの値が再計算される (情報の流れの向きと参照関係は逆) ElementE =======> HandleH ElementEはHandleHを参照している。 ElementEのフィールド入力値が変更されるとhandleHを保持するInputに所属するSignalで更新イベントが発生する (情報の流れの向きと参照関係は同じ) valueA ........> valueB valueAがvalueBとして使われている。純粋な値のコピー。
- btnを押したときに、evalOutへの出力はされるのに、fldを変更したときに、evalOutへ出力されないのはなぜか?それは、全体構造を良く見るとわかるように、fldが変更されたとき、inp.signalが変化し、その値をもとにbtnが再構築されるから。btnInp.signalはinp.signalに依存していない。inp.signal.stringの(純粋な)値を使って構築されている。
関連エントリ
「Elmでやってみる」シリーズのまとめエントリ - uehaj's blog
- 作者: Miran Lipovača,田中英行,村主崇行
- 出版社/メーカー: オーム社
- 発売日: 2012/05/23
- メディア: 単行本(ソフトカバー)
- 購入: 25人 クリック: 580回
- この商品を含むブログ (67件) を見る
- 作者: Miran Lipovaca
- 出版社/メーカー: オーム社
- 発売日: 2012/09/21
- メディア: Kindle版
- 購入: 4人 クリック: 9回
- この商品を含むブログを見る
- 作者: Graham Hutton,山本和彦
- 出版社/メーカー: オーム社
- 発売日: 2009/11/11
- メディア: 単行本(ソフトカバー)
- 購入: 14人 クリック: 503回
- この商品を含むブログ (117件) を見る