Posted at

テンプレートエンジン ede を使ってみる

More than 3 years have passed since last update.

edeはHaskellの軽量テンプレート言語です。edeのルールに従って書かれたテキストにHaskellのデータ構造を使ってレンダリングしてテキストを生成することが出来ます。実際は主にHTMLページの生成とかに使うんだと思います。

HTMLの生成はHaskellではshakespeare, blaze-html, lucidなどが有名ですがどれも書き方が独特です。エンジニア以外の人と作業する場合はHTMLは出来るだけそのままに最低限のテンプレートの記法で済むようなものがいいでしょう。edeはMustacheベースのテンプレートでHaskeller以外の人にもわかりやすいものになっています。

スクリーンショット 2015-11-21 4.50.35.png

例えばHTMLでこういうテーブルを表示するとしましょう。データはHaskell側から渡します。

data Todo = Todo

{ title :: String
, done :: Bool
}

todos :: [Todo]
todos = [ Todo "買い物に行く" False
, Todo "ゲームをする" True
, Todo "Haskellを書く" True
]

さっそくedeのテンプレートを書きます


index.html

<!DOCTYPE html>

<html>
<head>
<title>{{ title }}</title>
</head>
<body>
<table border="1">
<tr>
<th>#</th>
<th>Title</th>
<th>Done</th>
</tr>
{% for todo in todos %}
<tr>
<td>{{ todo.index }}</td>
<td>{{ todo.value.title }}</td>
<td>
{% if todo.value.done %}

{% else %}

{% endif %}
</td>
</tr>
{% endfor %}
</table>
</body>
</html>

ほとんどHTMLのままで必要なところだけテンプレート記法が使われてるのがわかると思います。Spockを使って実際にWebサーバー経由で表示してみるプログラムを書いてみます。


app/Main.hs

{-# LANGUAGE OverloadedStrings #-}

{-# LANGUAGE DeriveGeneric #-}

module Main where

import Data.Aeson
import Data.Text.Lazy (toStrict)
import GHC.Generics
import Text.EDE
import Web.Spock

data Todo = Todo
{ title :: String
, done :: Bool
} deriving Generic

instance ToJSON Todo

todos :: [Todo]
todos = [ Todo "買い物に行く" False
, Todo "ゲームをする" True
, Todo "Haskellを書く" True
]

main :: IO ()
main = do
tpl <- eitherParseFile "index.html"
let env = fromPairs [ "title" .= ("TODOリスト" :: Value)
, "todos" .= toJSON todos ]
body = either error toStrict $ tpl >>= (`eitherRender` env)
runSpock 3000 . spockT id $ do
get "/" $ html body


edeのテンプレートに渡すパラメータはAesonObjectである必要があるのでTodoToJSONのインスタンスになっています。実行してhttp://localhost:3000にアクセスすると先ほどのテーブルが表示されると思います。

HaskellにはlucidのようにモナドでHTMLを組み立てられるようなDSLもあって便利な一方で実際の開発ではHTMLはいろんな人がさわると思うのでedeのように薄いテンプレート言語が必要になるのだと思います。適材適所で使い分けていきたいですね