In message <20040512.214933.104029097.nobsun / sampou.org>, Nobuo Yamashita writes: >これは、Monad Raws の事かな。 > >(1) (return x) >>= f ≡ f x >(2) m >> = return ≡ m >(3) (m >>= f) >>= g ≡ m >>= (\ x -> f x >>= g) > >"All about monads" によれば、 > >(1) は return が >>= に関して左単位元になっているという主張 >(2) は return が >>= に関して右単位元になっているという主張、 >(3) は >>= に関する一種の結合法則のようなもの > >とあります。 ありがとうございます。 とりあえず、じゃかじゃか翻訳しました。 英語苦手人間なんで、かなり色々怪しいです。 もし時間があれば添削などして頂けると。。。うれしい。 特に動作関係を解説している部分がよく分かってないので・・・。 基本的に原文はセミコロンでエスケープ。 原文の内でコード部分はそのまま引用。 原文の下に日本語訳(のつもり)でやす。 (あ、イントロの直前の英文そのまま。) 分からないなりに、唯一、 「二つの計算は順に実行される。なぜなら二番目のものは一番目の結果を必要とするからだ。」 ってのが自分的には、少しきっかけを作ってくれたかもです。 1. 必要呼びという評価順序(非正格)だから書いた順序での実行ではない。 2. IO の様に評価順序が重要な場合には評価順序をプログラマが分かりやすい形で指定したい。 3. だからといって正格指定といった逃げはしないで美しい世界の中で完結させたい。 4. 勘違いしちゃいけないのは、非正格な評価順序は別に規則が無いのではなく、 必要なものから評価するという規則がある。 それがプログラムを書く側からは分かりにくい(追い切れない、ってか普通追う必要ないからね)だけ。 5. ってことは後の方で評価させたい式が前に評価させたい式の結果を必要とするように書けばいい。 6. でもそれを見えるように書くのでは無くって、包み隠して見えないようにすればいいかも。 そういうプログラミングパラダイム(?)といっていいかどうか知らないけど、そういうもの? って感じですかね? そりゃ全然違う〜落第〜とかダメ出しくらいます?(^^;; ----- ;Monadic Programming in Scheme Scheme における Monad 的プログラミング ; 1. Introduction ; 2. Serially-numbering monad in Scheme ; 3. Example ; 4. Generalizations to several monads ; 5. References and pointers to further discussion 1. イントロダクション 2. Scheme におけるシリアルナンバモナド 3. 例示 4. いろいろなモナドへの一般化 5. 参考とさらなる議論へのポインタ An example of a monadic programming in Scheme that juxtaposes Haskell code for a particular state monad with the corresponding Scheme code. ;Introduction * イントロダクション ;This article sprang from a question posted on comp.lang.functional about building of trees whose ;nodes are tagged with unique integers. "Ideally every term/node has to have its id associated with ;it at construction time," the poster said. "An obvious way to implement it in this ;[referentially-transparent] way is to have a collection of state which is passed along all the time ;to every function. But this seems rather tedious." この記事はユニークな整数によりタグ付けさたノードで木構造を構築するという comp.lang.functional にポストされた質問から生じたものである。 ”観念的に言えば、全ての項/ノード が、それ自体の構築された時間に関連するIDを持つ” とそのポストを行った人物は言っていた。 ”すぐに思い付く実装手段は[参照透明]な手段で、全ての時間全ての関数を通る状態の堆積を持つことだけど、 これはかなりつまらないものに思われる。” ;An imperative solution is obvious: introduce a global variable -- current_id -- and use its value ;to tag every allocated tree node, incrementing the variable afterwards. A functional solution is ;relatively straightforward too: every function that creates or operates on trees must take an extra ;argument -- the current value of the tagging counter. The function should return multiple values: ;the result of its computation and (perhaps incremented) value of the counter. The latter solution ;does seem to be tedious, requiring multiple values, and ugly. Is there a way to solve the problem ;purely functionally and elegantly? 命令(言語)的解法は明白で:大域変数 -- 現在のID -- を導入し、そしてその値を全てのアロケートされた 木のノードにタグ付けするのに使い、その後で変数はインクリメントするのだ。 関数(言語)的解法もどちらかと言えば直接的で:木を生成し操作する全ての関数がもう一つ余分の引数 -- タグ付け用カウンタの現在の値 -- を取るものである。 その関数は多値を返す必要がある。:その計算結果と(おそらくインクリメントされた)カウンタの値とをだ。 後者の解法はつまらないし多値を必要とするし、そして醜いものに思われる。 純粋に関数的かつエレガントにこの問題を解決する方法があるだろうか? ;A proposed Haskell solution [SN-monad] relied on monads to hide the counter holding the tag for a ;new node. The solution did seem to reach the goal. Building a tagged tree looked just as simple as ;building an untagged one. The counter variable was well-hidden. And yet it was there, invisibly ;threading through all computations. Rendering this solution in Scheme can be fun. 提案された Haskell の解法 [SN-monad] は、新しいノードのためのタグを保持するカウンタを 隠し持つモナドに依存するものだ。 この解法こそが到達すべきゴールに思われた。 タグ付けされた木の構築はちょうどタグ付けされていない木の構築と同じ様に簡素に見えたのだ。 そのカウンタ変数はうまく隠れていたし、その上全ての計算を通じて目には見えないスレッドがあった。 この解法を Scheme に翻訳するのはおもしろいものになるだろう。 ;Serially-numbering monad in Scheme * Scheme におけるシリアルナンバモナド ;We will cite the Haskell code in our Scheme code, for comparison and documentation. We will ;identify Haskell code by ;-- comments. 比較と考証のために、自身の Scheme コードの中に、その Haskell コードを例示しよう。 そこでは ;-- comments の様にして同じ意味の Haskell コードを示そう。 ;We start by defining a datatype for integer-tagged values. We could use records or associative ;lists; However, a simple pair will suffice: まず整数でタグ付けされた値のためのデータ型を定義することから始めよう。 レコードか連想リストを使うことができるだろうが、単純なペアで十分だ。 ;-- type Numbered a = (Int,a) (define (make-numbered-value tag val) (cons tag val)) ; accessors of the components of the value (define (nvalue-tag tv) (car tv)) (define (nvalue-val tv) (cdr tv)) ;We need to define a counting monad and its two fundamental operations: bind (aka >>=) and return. ;In Haskell, we write 計数モナドとその二つの基本演算: bind (別名 >>=) と return を定義する必要がある。 Haskell では次のように書かれる。 ;-- newtype NumberedM a = NumberedM (Int -> Numbered a) ;-- instance Monad NumberedM where ;-- NumberedM m >>= f = NumberedM $ \n -> let (n1,v) = (m n) ;-- NumberedM m' = f v ;-- (n1',v') = (m' n1) ;-- in (n1',v') ;-- return x = NumberedM $ \n -> (n,x) ;There are two ways of rendering that in Scheme. In this section, we will be discussing an easy way, ;which suffices as long we are working with a single monad, or do not mix objects of different ;monads. In that case, all the type tagging apparent in the Haskell solution is unnecessary. Our ;monad is simply a function Int -> Numbered a. A monad is a delayed computation: a lambda-expression ;in our case. It takes the value of the counter, performs whatever computation it is meant to do, ;and returns the result tagged with the (perhaps incremented) counter. Scheme に翻訳するのに二つの方法がある。 この節では、異なるモナドのオブジェクトを混合するのではなく、 単一のモナドで動作するのに十分なだけ簡単な方法で議論しよう。 この場合、 Haskell の解法では目に見える形でタグ付きしている型は全て必要ではない。 我々のモナドは単なる Int->Numberd a な関数である。 このモナドは遅延評価:我々の場合にはλ式である。 そのモナドはカウンタの値を取り、その意味するところの計算を実行し、 そして(おそらくインクリメントされた)カウンタでタグ付けされた結果を返す。 ;The two fundamental operations are introduced as: 二つの基本演算は以下のように導入される。 ;-- return:: a -> NumberedM a (define (return val) (lambda (curr_counter) (make-numbered-value curr_counter val))) ;and そして ;-- (>>=):: NumberedM a -> (a -> NumberedM b) -> NumberedM b (define (>>= m f) (lambda (curr_counter) (let* ((m_result (m curr_counter)) (n1 (nvalue-tag m_result)) ; result of the delayed computation (v (nvalue-val m_result)) ; represented by m (m1 (f v)) ; feed the result to f, get another m1 ) (m1 n1)))) ; The result of the bigger monad ;The bind operator, >>=, makes a "bigger" monad out of a monad m and a function f (which yields a ;monad when applied to a value). That bigger monad is a delayed computation that incorporates both ;computations: those represented by m and by f. The job of the bind operator is to merge these ;computations and to take care of passing the state -- the counter in our case -- from one ;computation to the other. The code of the bind operator first applies the monad m to the ;curr_counter. This executes the delayed computation and gives us its result and the new counter. We ;apply f to the result of the delayed computation m and get another monad, m1. The latter is a ;function, which we apply to the counter returned by m and get a tagged value -- the result of the ;combined monad. bind 演算、>>= はモナド m と関数 f とから、”より大きな”モナドをつくる。 (この関数 f は値に適用された時にモナドを生み出す) その、より大きなモナドは m と、 f で表現される計算との両方を組み込んだ遅延評価である。 bind 演算の仕事は、これらの計算を合成して、 ある計算から別の計算への状態の遷移 -- 今の場合はカウンタ -- を扱うことだ。 bind 演算のコードはまず、モナド m を curr_counter に適用する。 これが遅延評価を実行し、我々にその結果と新たなカウンタを与える。 我々は f を遅延評価 m の結果に適用してもう一つ別のモナド m1 を手に入れる。 後者は m により返されたカウンタに適用した関数で、タグ付けされた値 -- 結果として複合モナドを手に入れる。 ;It is easy to verify that monad axioms are satisfied. Indeed, return is the left and the right unit ;of bind, that is, モナド公理が満足される事を確認するのはたやすい。 実際、 return は bind の左単位元と右単位元になっている。 すなわち、 (>>= (return v) f) ==is-the-same-as==> (f v) (>>= m (lambda (v) (return v))) ==is-the-same-as==> m ;Indeed, the direct inspection shows that (>>= m (lambda (v) (return v))) takes the result of (m ;counter) into two parts and puts them back together again. 実際、この直接的な検査は (>>= m (lambda (v) (return v))) が (m counter) の結果を取って、 二つの部分に分け、再びそれらを一緒に戻すということを示す。??????? ;The bind operation is clearly associative, bind 演算は明らかに結合則を満たす。 (>>= m (lambda (v) (>>= (f v) g))) <==is-the-same-as==> (>>= (>>= m f) g) ; v is not free in g nor in f ;Thus we have indeed built a monad. Besides the canonical return and bind operations, we need one ;more operation (so-called morphism): このようにして我々はモナドを構築した。規準的な return 演算と bind 演算に加えて、 さらにもう一つの演算 (モルフィズムと呼ばれる) ものを必要とする。 ;-- get the current id and increment it ;-- incr:: NumberedM Int ;-- incr = NumberedM $ \n -> (n+1,n) (define incr (lambda (n) (make-numbered-value (+ 1 n) n))) ;We also need an operation to run the monad, that is, to take the delayed computation represented by ;the monad and execute it. In our case, a delayed computation needs a value -- the initial value of ;the tagging counter. 我々にはまた、モナドを走らせる演算、つまりモナドにより表現される遅延計算(オブジェクト)を取り、 それを実行する演算が必要である。 今の場合、遅延評価はある値 - タグ付きカウンタの初期値を必要とする。 ;-- run_numberedM:: NumberedM a -> Int -> Numbered a ;-- run_numberedM (NumberedM m) init_count = m init_count (define (runM m init-counter) (m init-counter)) ;The result of runM is a numbered value, a pair of the final counter and the final value. runM の結果は数値化された値、最終カウンタと最終値とのペアである。 ;Example * 例 ;Let us now see what this painstakingly defined monad can do for us. Let us consider trees この厳密に定義されたモナドが我々にとって何をもたらすかを見てみよう。 木について考えることにする。 ;-- data Tree a = Nd a (Forest a) deriving Show ;-- type Forest a = [Tree a] ;In Scheme terms, a node is (value . forest) where forest is (node ...). A node is a list whose head ;is the node's value and the tail is the list of its children. A forest is a list of zero or more ;nodes. Scheme 用語では、 forest を (node ...) とした場合、ノードは (value . forest) である。 ノードはリストであり、その head はノードの値であり tail はその子リストである。 forest は 0 以上のノードのリストである。 ;Let's define a function to make a node -- a node that is tagged with a unique integer: では、ユニークな整数でタグ付けされたノードを作る関数を定義しよう。 (define (make-node val kids) (>>= incr (lambda (counter) (return (cons (make-numbered-value counter val) kids))))) ;That's what the original posted wanted: associate with each node a unique integer at ;node-construction time. このことは最初のポストが求めたもの: ノードが構成された時にユニークな整数を各ノードに関連付けるものである。 ;The code is hardly surprising: we obtain the value of the "global" counter, increment the counter ;and build a node out of the tagged value and the list of kids. Only there is no global counter and ;no mutation. このコードは非常に驚くべきものである。 我々は”大域的な”カウンタの値を得、そのカウンタをインクリメントし、 タグ付けされた値と子リストとからノードを構築する。 そこには大域的なカウンタも変更(副作用)もない。 ;Now let's try to build a full binary tree, where the value of each node is that node's height: 今、各ノードの値がそのノードの高さであるような、 完全二分木を構築しよう。 ;-- make_btree 0 = make_node 0 [] ;-- make_btree depth = do { ;-- left <- make_btree (depth -1); ;-- right <- make_btree (depth -1); ;-- make_node depth [left, right] ;-- } ;This function that takes the desired depth and returns a monad, which -- when run -- produces a ;tagged binary tree. ;The first attempt In Scheme is: この関数は望ましい深さを取り、 -- 走ったときに -- タグ付けされた二分岐を生成するようなモナドを返す。 最初の試みは Scheme では以下である。 (define (build-btree-r depth) (if (zero? depth) (make-node depth '()) (>>= (build-btree-r (- depth 1)) (lambda (left-branch) (>>= (build-btree-r (- depth 1)) (lambda (right-branch) (make-node depth (list left-branch right-branch)))))))) ;A syntactic sugar is direly needed. First, we introduce letM: 構文糖衣がさしあたって必要になる。 まず、 letM を導入する。 (letM ((name initializer)) expression) ==> (>>= initializer (lambda (name) expression)) ;Compare with a regular let: 正規の let と比較しよう。 (let ((name initializer)) body) ==> (apply (lambda (name) body) (list initializer)) ;There are some differences, however: for one thing, letM takes only one binding, while the regular ;let may take several. The body of letM is a single expression that has to evaluate to a monad. The ;let's body can have several expressions. There is a deep reason for these differences. If a ;let-form has several bindings, their initializing expressions are evaluated in an undefined order. ;Such non-determinism does not exits in the monadic world: since the evaluation of our monad ;involves threading of a state through all computations, the computations must execute in the ;precisely defined order, one after another. Monads introduce sequencing (single-threading) into the ;functional world. In that sense, monadic computation emulates imperative computation. However, in ;the imperative world, statements execute in the order they are written because such is the ;semantics of an imperative language. We trust the system honoring our statement order, because the ;order of executing mutations is crucial to knowing the global state of the system at any point in ;time. In contrast, in monadic world there is no really global state. A state is explicitly passed ;from one computation to another. Two computations are executed in order because the second needs ;the result of the first. いくつかの相違があるが、一つには、 letM はただ一つの束縛を取るのに対し、 正規の let はいくつかの束縛をとれる。 letM の本体は単一の式でモナドへ評価するものである。let の本体はいくつもの式を取り得る。 これらの違いには深遠な理由がある。 let 形式がいくつかの束縛を持つ場合、これらの初期化式の評価順序は決まっていない。 そうした非決定性がモナド的世界には存在しない。 我々のモナドの評価は全ての計算を通じて状態のスレッド化を伴うので、 その計算は正確に決められた順に次々と実行されなければならない。 モナドは関数世界の中に逐次(シングルスレッド)を導入する。 この意味で、モナド的計算は命令(言語)的計算をエミュレートする。 この命令(言語)的世界では文は命令言語の意味論であるので、それらが書かれた順に実行される。 我々はこのシステムが我々の記述順序を尊重してくれることを信頼できる。 なぜならこの実行順の変更は、このシステムの任意の時点での 大域的な状態を知る上で極めて重要になるからだ。 対照的にモナド的世界には現実の大域的状態というものはない。 ある状態は明白に、ある計算から別の計算へ通過される。?????? 二つの計算は順に実行される。なぜなら二番目のものは一番目の結果を必要とするからだ。 (define-macro letM (lambda (binding expr) (apply (lambda (name-val) (apply (lambda (name initializer) `(>>= ,initializer (lambda (,name) ,expr))) name-val)) binding))) ;We can also introduce an even sweeter form letM*: 我々はまた、よりいっそうの糖衣形式 letM* を導入できる。 (letM* (binding binding ...) expr) ==> (letM (binding) (letM* (binding ...) expr)) ;which relates to letM exactly as let* relates to let. これは let* の letM との関連は let* の let との関連と厳密に同じである。 (define-macro letM* (lambda (bindings expr) (if (and (pair? bindings) (pair? (cdr bindings))) `(letM ,(list (car bindings)) (letM* ,(cdr bindings) ,expr)) `(letM ,bindings ,expr)))) ;With these sweets, we can re-write our build-btree as これらの糖衣構文があれば、我々の build-btree を次の様に書き直すことができる。 (define (build-btree depth) (if (zero? depth) (make-node depth '()) (letM* ((left-branch (build-btree (- depth 1))) (right-branch (build-btree (- depth 1)))) (make-node depth (list left-branch right-branch))))) ;Note the code does not explicitly mention any counter at all! Let's see how it runs: このコードはいかなるカウンタにも明示的には触れていない! では、どのように走るかをみてみよう。 > (pp (runM (build-btree 3) 100)) (115 (114 . 3) ((106 . 2) ((102 . 1) ((100 . 0)) ((101 . 0))) ((105 . 1) ((103 . 0)) ((104 . 0)))) ((113 . 2) ((109 . 1) ((107 . 0)) ((108 . 0))) ((112 . 1) ((110 . 0)) ((111 . 0))))) ;Each node of the tree is uniquely tagged indeed. The counter starts at 100 and counts forward. The ;value of the node (the second component of a pair) is that node's height. The node tagged with 114 ;is the root node; its value is 3. Number 115 is the final value of the counter. 木の各ノードはちゃんとユニークにタグ付けされている。そのカウンタは 100 で始まり、 count forward する。 ノードの値(対の第二要素)はノードの高さである。 114 でタグ付けされたノードは根で、その値は 3 だ。数値 115 はカウンタの最終値である。 ;It is interesting to compare the build-btree code with a code that constructs a regular, non-tagged ;full binary tree: build-btree のコードと正規のタグ無しの完全二分木を構築するコードとを比較してみると面白い。 (define (build-btree-c depth) (if (zero? depth) (cons depth '()) (let ((left-branch (build-btree-c (- depth 1))) (right-branch (build-btree-c (- depth 1)))) (cons depth (list left-branch right-branch))))) > (pp (build-btree-c 3)) (3 (2 (1 (0) (0)) (1 (0) (0))) (2 (1 (0) (0)) (1 (0) (0)))) ;The similarity is staggering. It seems we have achieved our goal: building of a tagged tree looks ;almost identical to that of an un-tagged tree. The tagging counter is well-hidden from view. The ;counter does not get in the way and does not clutter the look and feel of the computation. The ;letM* form reminds us however that the counter does exist. The form emphasizes the important ;difference between the two functions. The function build-btree-c constructs left and right branches ;in an indeterminate order. The branches could even be built in parallel, hardware permitting. The ;order does not matter -- the result is the same regardless of the sequence. The form letM* however ;makes the computation in the function build-btree strictly sequential: there, the right branch is ;constructed strictly after the left branch, with their parent node following. この類似性には驚かされる。どうやら我々のゴールにたどり着いたように思える。 つまり、タグ付きの木を構成することがタグ付けされていない木の構築とほとんど同一に見える タグ付けをするカウンタは視野から十分に隠されている。 このカウンタは邪魔されていて取得できないし、計算の見た感じも取り散らかされていない。 しかしながら letM* 形式がカウンタが存在していることを思い出させる。 この形式は二つの関数の間に重要な相違を強調するものだ。 関数 build-btree-c は左右の枝を不確定な順番で構成する。 ハードウェアさえ許すなら、枝は並列にすら構築され得る。 順序は問題ではない -- その結果はその順序については無頓着だ。 しかし、形式 letM* は関数 build-btree においては、厳密に順序通り: 右の枝は左の枝より厳密に後で、引き続いて親のノードが構成されるように計算結果をつくり出す。 ;Generalizations to several monads * いろいろなモナドへの一般化 ;Thanks to type classes, Haskell monads are more general than the previous sections showed. We can ;mix several monads in the same expression: 型クラスのおかげで、 Haskell のモナドは前節で示したものより一般的である。 同じ式の中にいくつかのモナドを混ぜ合わせることができる。 f = do { putStrLn "Enter a number: "; n <- readLn; all_n <- return [1..n]; evens <- return $ all_n >>= (\i -> if (i `rem` 2) == 0 then return i else fail "odd"); return evens } main = f ;This silly code "prints" all even numbers up to the one entered by the user. There are two monads ;in this fragment: IO a monad and [a] monad: a list is also a monad. Expression return [1..n] ;returns an object of a type IO [Int] whereas return i yields a monad [Int]. The outer return in the ;same expression returns IO [Int]. It is remarkable that a Haskell compiler is able to figure out ;which monad each return function yields. The compiler bases its decision on the type of the result ;expected in each case. To render the above code in Scheme, we have to explicitly identify monad ;operations of different monads: このばかげたコードはユーザから入力された数値から 1 までの全偶数を”印字”する。 このコード片には二つのモナド: IO モナドと [a] モナド(そう、リストもまたモナドだ)とがある。 式 return [1..n] が IO [Int] 型のオブジェクトを返すが、それに対して return i が [Int] モナドを生み出す。 同じ式の外側の return は IO [Int] を返す。 注目すべきは、 Haskell コンパイラが各 return 関数が生み出すモナドを理解できるということだ。 コンパイラはその決定については、各々のケースで期待される結果の型に基礎を置いている。 上記のコードを Scheme に翻訳するために、異なるモナドを明確に識別するモナド演算子を手に入れよう。 (define f (IO::>> (put-line "Enter a number: ") (IO::>>= (read-int) (lambda (n) (IO::>>= (IO::return (iota 1 n)) (lambda (all-n) (IO::>>= (IO::return (List:>>= all-n (lambda (i) (if (even? i) (List::return i) (List::fail "odd"))))) (lambda (evens) (IO::return evens))))))))) ;However, even in this pathologically mixed case we note that some sub-expressions use only the IO ;monad, and one subexpression uses only the List monad. Therefore, we can write: しかしながら、この病的な程の mixed case においてさえ、いくつかの部分式が IO モナドだけを使っており、 一つの部分式がリストモナドだけを使っていることに気付く。 したがって、次のように書くことができるだろう。 (define f (let ((>>= IO::>>=) (return IO::return)) ; define the "current" monad (beginM (put-line "Enter a number: ") (letM* ((n (read-int)) (all-n (return (iota 1 n))) (evens (return ; re-define the current monad (let ((>>= List::>>=) (return List::return) (fail List::fail)) (letM ((i all-n)) (if (even? i) (return i) (fail "odd")))))) ) (return evens))))) ;This is not as elegant as in Haskell, yet is readable. With a bit of syntactic sugar and a module ;system similar to that of DrScheme, we could even replace これは Haskell における程エレガントではないが、読むことはできよう。 若干の構文糖衣と DrScheme と同様なモジュールシステムとを持っていれば、 (let ((>>= IO::>>=) (return IO::return)) ;with (using IO). を (using IO) で置き換えることすらできる。 ---- ;References and pointers to further discussion ; ;[SN-monad] A serially-numbering monad. ;< ../Haskell/misc.html#serially-numbering-monad> ; ;[MShell] UNIX pipes as IO monads. Monadic i/o and UNIX shell programming. ;< ../Computation/monadic-shell.html> ; ;[MScheme-IO] Monadic scheme of I/O ;< misc.html#monadic-io> ; ;[MP-Scheme] Monadic programming in Scheme: an example. ;An article posted on a newsgroup comp.lang.scheme on Wed, 10 Oct 2001 20:27:21 -0700. ; ;[MV-Reif] Multiple values as an effect. Monadic reflection and reification. ;< misc.html#multiple-value-effect> ; ;Last updated March 1, 2004 ; ;This site's top page is http://pobox.com/~oleg/ftp/ ; ;oleg-at-pobox.com or oleg-at-acm.org or oleg-at-computer.org ;Your comments, problem reports, questions are very welcome! ; ;Converted from SXML by SXML->HTML ; -- ML: haskell-jp / quickml.com 使い方: http://QuickML.com/