uehaj's blog

Grな日々 - GroovyとかGrailsとかElmとかRustとかHaskellとかReactとかFregeとかJavaとか -

elmでやってみるシリーズ7: elm-htmlでTwitter Bootstrapを適用

Elmでやってみるシリーズ7: elm-htmlでTwitter Bootstrapを適用。

つい先日、「Blazing Fast HTML」と銘打って、elm-htmlライブラリが公開されました。これはElmでDOMツリーを構築・更新するための低レベルライブラリであり、Virtual DOMという技術を使っているので非常に画面更新速度が速いそうです。SPA(Single Page Application)ではDOMの更新速度が重要になりますが、Elmは純粋関数型・イミュータブルデータなのでそのことを利用してさらに効率良く実装できるそうな。

従来、ElmはCSSとの連携はあまり重視されておらず、「ElmはCanvasを使ったアニメーションが得意」とされてきましたが、現代的な見た目のHTMLベースのアプリも自在に開発できるようになる、という道筋の第一歩なわけです。まだ未成熟ですがね。

個人的にTwitter BootstrapなどのCSSフレームワークとElmとの連携に興味があったので、今回試してみました。

やったことは以下のとおり。

  1. elm-getでelm-htmlをインストール
    • elm-get install evancz/elm-html 0.1.2
  2. HtmlTest.elmを作る(後述)
  3. 以下のように--only-jsオプションを付けてelmコードをコンパイル。なお、--only-jsを使用する場合、呼び出すためにモジュール名が必要なので冒頭でmodule宣言が必要になる。
    • elm --make --only-js HtmlTest.elm
  4. Twitter Bootstrapを使用するindex.htmlをこちらの「Embed in HTML」を見て適当につくる。index.htmlの内容はこちらを表示してビューソースしてみてください。
  5. index.htmlとHtmlTest.jsをgithub pagesにpush。

処理の内容としては、マウスのX座標、Y座標の過去10個分をテーブルにして表示するというもの。

コード(HtmlTest.elm)は以下のとおり。

module HtmlTest where -- --js-onlyをする場合モジュール宣言は必須

import Html (..)
import Mouse

-- マウス座標データを一行分の<tr>に変換
data2line : (Int,Int) -> Html
data2line (x,y) = node "tr" [] []
         [ node "td" [] [] [text <| show x]
         , node "td" [] [] [text <| show y]
         ]

-- テーブルを作る
tbl : [(Int,Int)] -> Html
tbl dat = node "table" ["className" := "table table-striped table-bordered table-condensed"] []
      [ node "thead" [] [] [
          node "tr" [] []
              [ node "th" [] [] [text "Mouse X"]
              , node "th" [] [] [text "Mouse Y"]
            ]
          ]
      , node "tbody" [] []
         (map data2line dat)
      ]

-- アンカータグによるリンクを作るユーティリティ関数
linkTo txt url = node "a" ["href":=url] [] [text txt]
-- ボタンのように装飾をしたリンクを作るユーティリティ関数
buttonLinkTo txt url = node "a" ["href":=url,"className":="btn btn-primary btn-lg"] [] [text txt]


-- 画面を作る
body : [(Int,Int)] -> Html
body dat = node "div" ["className":="navbar navbar-default navbar-fixed-top"] ["padding-top":= px 10] [
         node "div" ["className":="container"] [] [
           node "div" ["className":="jumbotron"] []
             [ node "h1" [] [] [ text "Elm/Twitter Bootstrap" ]
             , node "p" [] [] [ "Elm-html" `linkTo` "https://github.com/evancz/elm-html"
                              , text "で"
                              , "Twitter bootstrap" `linkTo` "http://getbootstrap.com/" 
                              , text "連携しています。"]
             , node "p" [] []
             [ "もっと学ぼう" `buttonLinkTo` "http://elm-lang.org/"
             , "ソースコード" `buttonLinkTo` "HtmlTest.elm"
             ]
           ]
           , tbl dat
         ]
       ]

-- 画面を表示する
display : [(Int, Int)] -> Element
display list = body list |> toElement 200 200

-- マウス座標のシグナル値(x,y)をliftして与えて画面を表示する
main : Signal Element
main = display <~ foldp (\it acc -> take 10 (it :: acc)) [] Mouse.position

実行画面(操作可能)

全画面で実行する場合はこちら
プロジェクト全体はこちらのgh-pagesブランチからどうぞ。

気づいたこと

  • elm-htmlはGroovyのマークアップビルダーみたいなもの。
  • elm-htmlの出力はHtml(DOM)であり、それを変更する手段は提供されていない。なのでコード的には毎回全体を何も考えずに宣言的に生成する。しかし、Elmが完全に純粋であることも利用して、Vitual DOMを通じて、実DOMに対して最低限の差分のみが効率良く適用されるという話。
  • elm-htmlの記述はHTMLと一対一対応で、冗長度が高いが、elm-htmlの位置付けは、より高機能でより抽象度の高いライブラリ作成の基盤になるための低レベルライブラリ、というものなのでこれはこれで良い。
  • elm-htmlはいわゆる非標準ライブラリであり、しかも--only-jsでHTMLと連携させるので、share-elmなどでは公開できない。なのでgithub-pagesを使用して公開してみた。
  • --only-jsを前提とするとelm-serverでホットリロードできない。もしくはやりかたがわからない。
  • elm-htmlのHtmlとGraphics.Input(.Fields)との連携・関係はまだ理解できていない。elm-htmlだけでhandleを使えるから、こっちで閉じてやるんだろうか。そうじゃないとしたらElementやfieldをHtmlに入れる仕組みが必要なはずだが。
  • elm-htmlではcssも要素の属性も基本的には文字列でしかない。本来なら強い型付けをして欲しいものですが、将来開発されるであろうelm-html上に構築される何かに期待。
  • 合成と共有部分の切り出し、共有化、抽象化というプログラミングの本質的な強力さが現状でも享受できる。html,js,cssを使っていたWebアプリ開発暗黒時代の夜明け
  • Elmで外付けスタイルシートを定義することはできない。本来はElmがLESSやSassの代替になって欲しいものである。*1
  • CSSクラスの指定は「"className":=」、floatは「cssFloat」とするらしい。このような違いはJavaScriptでDOMをいじる場合と同様らしい。

関連エントリ


「Elmでやってみる」シリーズのまとめエントリ - uehaj's blog



すごいHaskellたのしく学ぼう!

すごいHaskellたのしく学ぼう!

すごいHaskellたのしく学ぼう!

すごいHaskellたのしく学ぼう!

*1:と思ったが、そもそも、DOMを直接いじってスタイルが変更できるのだから、外付けスタイルシートなどはまったくいらなくなる…かとも思ったが、JSでトラバースしてスタイルつけるより、クラス名だけつけてブラウザに任せた方が速いと思うので、やっぱり何かしら必要かも。よくわからない。