きらきらと落ちてくる星の欠片
そのあとを星屑が緩やかな舞いを見せていく
もたらされるはさまざまなもの あまりにも多くのもの
それは――星の贈り物
星は何を望むのだろう?
[topic:Haskell]
これは Haskell Advent Calendar 2012 8日目の記事です。
みなさんは HTML 文書や XML 文書などを作成する際、どのような書式を使っているでしょうか? ベタに手書きしているでしょうか? Markdown やブログ独自の書式、Wiki 記法などの軽量のマークアップ言語を使っているでしょうか? Haskell などの言語上に構築した DSL を使っているでしょうか? それとも Pandoc を使って他の文書から変換しているでしょうか?
私は、今、 HSXML という Haskell の言語内 DSL を使ってこの文章を書いています。HSXML は、 SXML と呼ばれる「S 式を使って XML を表現する形式」の Haskell 版です。( HSXML のソースコード ) ……と、言葉だけで書いても分からないと思うので、例を見てみましょう。
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE OverloadedStrings #-}
module Main where
import HSXML
import HSXML_doc
import HSXML_ext (title)
import HSXML_HTML
import Prelude hiding (head, div)
testDoc =
(document
(head
(title "buildbox を使おう")
(meta_tag (description "generated HTML")))
(body
(h1 "buildbox の紹介")
(div (attr (title "buildbox の紹介と使用例"))
(p
(a (attr (href (URL "http://hackage.haskell.org/package/buildbox")))
"buildbox")
"は、" (strong "Haskell 製の継続的インテグレーションツール") "です。"
)
(p
"buildbox は"
(a (attr (href (URL "http://disciple.ouroborus.net/wiki/Development/Building")))
"ddc という言語処理系の開発")
"や"
(a (attr (href (URL "https://github.com/AccelerateHS/accelerate-buildbot")))
"accelerate というライブラリの開発")
"などで使われています。"
"また、現在も継続して使われているかどうか分かりませんが、"
(a (attr (href (URL "https://github.com/ghc/packages-dph/tree/master/dph-buildbot"))
(title "GHC が darcs で開発されていた時代の DPH ライブラリと repa の buildbot"))
"DPH の開発")
"や"
(a (attr (href (URL "http://stackoverflow.com/questions/4000005/lightweight-continuous-integration-for-a-centrally-haskell-darcs-toolchain"))
(title "今は亡き repa 専用の buildbot に関する情報"))
"repa の開発")
"にも使われていました。"
))
(p "なお、GHC の開発には、"
(a (attr (href (URL "http://hackage.haskell.org/trac/ghc/wiki/Builder")))
"builder")
"と呼ばれる独自のツールが使われています。")
(hr)))
main = runHTMLIO $ run_root testDoc
a (DC attrs :: DC CT_block d) body = inline_inline tr mempty body
where tr (DC body) = cdata_sep $
DC $ emit_elem "a" (Just attrs) (Just body) testDoc の定義が S 式になっていますね。この S 式の部分が HSXML です。main 変数では、この HSXML から HTML 文書を生成しています。
HSXML で使われている要素や属性などのタグは、全て Haskell の関数です。Haskell の関数なので、ただのS式とは違い「ブロック(レベル)要素として扱われる、インライン要素として扱われる、属性として扱われる、ブロック要素を持てる、インライン要素を持てる」などといった型があります。
例えば a 要素は、「属性とインライン要素を持つ、インライン要素」として定義されています。
*Main> :t a
a :: (MDoc d, Build (DC CT_inline d) t,
BuildR t DC CT_inline d) =>
DC CT_block d -> DC CT_inline d -> t
*Main> :m HSXML
Prelude HSXML> :t attr
attr
:: (HSXML_doc.MDoc d, Build (DC CT_battr d) r,
BuildR r DC CT_block d) =>
DC CT_battr d -> r
Prelude HSXML> :t href
href :: HSXML_doc.MDoc d => HSXML_doc.URL -> DC CT_battr d 同様に div 要素は「属性とブロック要素を持つ、ブロック要素」、p 要素は「インライン要素を持つインライン要素」として定義されています。
-- The body of P has the content Inline, but the P element itself
-- is in the Block context
p x = block_inline (wrap_tag "p") mempty x
〜 略 〜
-- HTML-visible blocking of block-level elements.
-- It is useful to set various attributes/styles on the enclosed
-- elements
div ((DC attrs)::d) body = block_block tr mempty (body::d)
where tr (DC body) =
DC $ emit_elem "div" (Just attrs) (Just body)
HSXML> :t div
div
:: (MDoc d1, Build (DC CT_block d1) t,
BuildR t DC CT_block d1) =>
DC CT_block d1 -> DC CT_block d1 -> t
HSXML> :t p
p :: (MDoc d, Build (DC CT_inline d) t,
BuildR t DC CT_block d) =>
DC CT_inline d -> t ここまで見て来たように、HSXML ではブロック要素とインライン要素、属性はそれぞれ別の型です。ですが、上の例で使われている HSXML_ext モジュール title のように、あるタグを要素としても属性としても扱いたくなることもあります。同様に、ブロック要素としてもインライン要素としても扱えるようなタグが欲しくなることがあります。このような場合、型クラスを用いて多相的に振る舞うようにタグを定義します。
例えば、title タグは、以下のような「ブロック要素やインライン要素、属性、それぞれの型に対するインスタンスを定義した型クラス」を利用することで「ブロック要素としてもインライン要素としても属性としても扱える」ように定義されています。
-- Title can be either
-- - a block-level element whose content is CT_inline
-- - an attribute (whose content is, therefore, CT_attr)
-- - an inline element, in which case it is the same as tspan
-- (however, "title ..." is more informative than "tspan ...")
class MkTitle ctx where
type MkTitleI ctx :: *
mk_title :: MDoc d => DC (MkTitleI ctx) d -> DC ctx d
instance MkTitle CT_block where
type MkTitleI CT_block = CT_inline
mk_title (DC body) =
DC $ emit_elem "title" Nothing (Just body)
instance MkTitle CT_battr where
type MkTitleI CT_battr = CT_attr
mk_title (DC body) = DC $ emit_attr "title" body
instance MkTitle CT_inline where
type MkTitleI CT_inline = CT_inline
mk_title = id
title x = build mk_title dc_empty x 同様に、Quotation モジュール (小文字の quote.hs) の quote タグも、ブロック要素(blockquote 要素)とインライン要素(q 要素)の二つの役割を持つように定義されています。
-- We define two different ways of rendering quote based on the context:
-- inline vs block.
-- The children of the quote are in the same context as the quote itself.
-- In addition, quote takes attributes, a (DC CT_block d) document
class MkQuote ctx where
mk_quote :: MDoc d => DC CT_block d -> DC ctx d -> DC ctx d
instance MkQuote CT_inline where
mk_quote (DC attrs) (DC body) =
cdata_sep . DC $ emit_elem "q" (Just attrs) (Just body)
instance MkQuote CT_block where
mk_quote (DC attrs) (DC body) =
DC $ emit_elem "blockquote" (Just attrs) (Just body)
quote attrs body = build (mk_quote attrs) dc_empty body 多相的に振る舞うタグを定義できることの他に、もう一つ忘れてはならないことがあります。それは(引数を取る) HSXML のタグが polyvariadic な(多相的かつ可変長な引数を取る)関数 であることです。例えば上の HSXML 文書の例にあったように、p 関数の引数として任意の数の文字列やタグを渡したり、attr 関数の引数として任意の数の属性を渡したりできます。
HSXML のタグの polyvariadic な関数としての振る舞いは、Monoid を拡張したクラスで定義されています。この辺をきちんと説明しようとするとそれだけで一つの記事になってしまうので、ここでは説明しません。とりあえず HSXML のタグが polyvariadic な関数であることと、そのために Monoid を拡張したクラスを使っていることが分かればそれで十分です。
HSXML での polyvariadic な関数の実現方法ついてどうしても気になる方は、自分でソースコードを見て調べてみてください。その場合、以下の記事などが参考になるかもしれません。
Haskell で可変長引数を扱う方法 (togetter のまとめ)
The Expression Problem (Haskell Advent Calendar 2010 の記事)
型クラスで tagless DSL メタプログラミング (Haskell Advent Calendar 2011 の記事)
ここまでで、HSXML の基本的な使い方について説明しました。あとは HSXML モジュールや HSXML_ext モジュールで定義されているタグ、提供されているサンプル・プログラムなどを見て試してみて下さい。これらを見ていけば、どのように使うか大体分かると思います。(この記事の作成に使用したソースコードも以下に置いておきます: diary_12.hs 、 ShortMarkUp.hs )
最後に、ここまで見てきたソースコードや GHCi のプロンプトの表示は、Template Haskell の準クォート(QuasiQuotes)を使って貼り付けています。Quote モジュール(大文字の Quote.hs)では、複数行に渡るテキストを簡単に貼り付けるための mup_text と mup_code という QuasiQuoter が用意されています。これらを使うことで、HSXML 中にソースコードやプロンプトの表示などをそのまま貼り付けることができます。
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
〜 略 〜
(p "href の定義は以下の通り。")
[mup_code|
href :: MDoc d => URL -> DC CT_battr d
href url = DC $ emit_attr "href" (emit_url url) |][topic:Haskell]
ITpro に 本物のプログラマはHaskellを使う - 第57回 機能テストや性能テストをCabalで自動化 が掲載されました。
今回は、これまでの回で説明した機能テストを行うプログラムや性能テストを行うプログラムを、Cabal を使って自動化する方法について説明します。また、機能テストを自動化する例では、コード網羅率を調べるための
かわちょアンテナ - 鞠絵あんてな - 可憐アンテナ - 亞里亞アンテナ
FoaF Explorer,
via
Web View,
via
)

このworkは、クリエイティブ・コモンズ・ライセンスの下でライセンスされています。
(背景画像、gift、他のライセンス下にあるもの、およびクリエイティブ・コモンズ・ライセンスでない work の二次的著作物を除く。)