Macrodown -MLが使えるML-

428 views
473 views

Published on

ML風の言語がマクロ定義に使える軽量マークアップ言語,Macrodownの紹介。ML勉強会( http://connpass.com/event/32752/ )の発表に使用したスライドです。

Published in: Engineering
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
428
On SlideShare
0
From Embeds
0
Number of Embeds
114
Actions
Shares
0
Downloads
0
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

Macrodown -MLが使えるML-

  1. 1. -ML が使える ML- 東京大学大学院 情報理工学系研究科 T. SUWA @bd_gfngfn gfngfn Meta Language Markup Language gfn
  2. 2. 突然ですが, 軽量マークアップ言語 をつくりました。
  3. 3. 突然ですが, 軽量マークアップ言語 をつくりました。 「……どうせMarkdown方言じゃないの?」
  4. 4. 突然ですが, 軽量マークアップ言語 をつくりました。 「……どうせMarkdown方言じゃないの?」 ――当たらずとも遠からず。
  5. 5. 突然ですが, 軽量マークアップ言語 をつくりました。 「……どうせMarkdown方言じゃないの?」 ――当たらずとも遠からず。 その名も Macrodown
  6. 6. 突然ですが, 軽量マークアップ言語 をつくりました。 「……どうせMarkdown方言じゃないの?」 ――当たらずとも遠からず。 その名も
  7. 7. -ML が使える ML- Meta Language Markup Language @bd_gfngfn gfngfn gfn東京大学大学院 情報理工学系研究科 T. SUWA
  8. 8. -ML が使える ML- Meta Language Markup Language @bd_gfngfn gfngfn gfn東京大学大学院 情報理工学系研究科 T. SUWA
  9. 9. なぜ をマークアップに?
  10. 10. 背景にある問題意識 TEX言語(とその上の LATEX)
  11. 11. 背景にある問題意識 TEX言語(とその上の LATEX) 激ヤバ プログラミング言語 つよい ライブラリ (あくまでも極端な解釈です) (語弊が大きい)
  12. 12. 背景にある問題意識 TEX言語(とその上の LATEX) 😄 ユーザが柔軟にマクロ定義できる 😫 構文も意味論も闇 • 構文木という概念がない • 字句解析と構文解析と評価が同時進行 • “合流性” なし • エラーの原因がわかりづらすぎる
  13. 13. 解決案 ⇒ 普通のプログラミング言語にしよう! ⇒ 静的型検査の力を借りよう! それなら……
  14. 14. しかない。
  15. 15. ML系の構文と意味論が マクロ定義に使える軽量マークアップ言語 ここでの「マクロ」はマークアップ言語的用法 マクロ≒函数 (defmacro …) 的な意味とは異なる Macrodown の概要
  16. 16. 基礎的なML系の性質を備える • 値呼び戦略 • 多相型推論(Hindley—Milner多相) • 代数的データ型 • パターンマッチ • モジュール(“非第1級”) Macrodown の概要
  17. 17. 出力 Macrodown インタプリタ 文書本体 (.mcrd) 基本的には文書用のマクロ集を文書と別に書く 文書用マクロ集 (.mcrdh) マクロ定義の方法
  18. 18. 出力 Macrodown インタプリタ 文書本体 (.mcrd) 基本的には文書用のマクロ集を文書と別に書く 文書用マクロ集 (.mcrdh) マクロ定義の方法 ML風の構文 で書ける LATEX+Markdown 風の 構文で書ける
  19. 19. • プログラム階層 - 主にマクロ定義に使う - マクロ集 (.mcrdh ) ではこの階層が主体 - ML 風の構文 • テキスト階層 - 主にマークアップに使う - 文書本体 (.mcrd ) ではこの階層が主体 - LATEX+Markdown 風の構文 構文の “階層”
  20. 20. 文書本体 main.mcrd 文書用マクロ集 first.mcrdh 例1 Hello, mcrd;! let mcrd = {Macrodown}Macrodown
  21. 21. 文書本体 main.mcrd 文書用マクロ集 first.mcrdh 例1 Hello, mcrd;! let mcrd = {Macrodown}Macrodown プログラム階層 テキスト階層
  22. 22. 文書本体 main.mcrd 文書用マクロ集 first.mcrdh 例1 Hello, mcrd;! let mcrd = {Macrodown}Macrodown プログラム階層 テキスト階層 入れ子の テキスト階層
  23. 23. 文書本体 main.mcrd 文書用マクロ集 first.mcrdh 例1 $ macrodown first.mcrdh main.mcrd –o output.txt Hello, mcrd;! let mcrd = {Macrodown}Macrodown
  24. 24. 出力 output.txt 文書本体 main.mcrd 文書用マクロ集 first.mcrdh 例1 Hello, mcrd;! Hello, Macrodown! $ macrodown first.mcrdh main.mcrd –o output.txt let mcrd = {Macrodown}Macrodown
  25. 25. 例2:文字列引数をとる let md s t = {Ma} ^ s ^ {r} ^ t ^ {down} Hello, Macrodown! Good bye, Markdown! 文字列結合 Ma downr Hello, md{c}{o}! Good bye, md{}{k}!
  26. 26. 例2:文字列引数をとる let md s t = {Ma@s;r@t;down} Hello, Macrodown! Good bye, Markdown! Hello, md{c}{o}! Good bye, md{}{k}! Ma@s;r@t;down テキスト階層内変数参照
  27. 27. ここまでは TEX/LATEX でも簡単なこと
  28. 28. 裏を返せば TEX/LATEX ではここまでしか 簡潔に実装できない
  29. 29. 例2:文字列引数をとる let md s t = {Ma@s;r@t;down} Hello, md{c}! Ma@s;r@t;down 型が合わないと……
  30. 30. 例2:文字列引数をとる ! [ERROR AT TYPECHECKER] at line 1, characters 7-13: this expression has type string -> string but is expected of type string. let md s t = {Ma@s;r@t;down} Hello, md{c}! ^^^^^^ 型が合わないと…… Ma@s;r@t;down
  31. 31. 例3:整数値引数をとる let repeat n s = if n <= 0 then {} else s ^ (repeat (n - 1) s) let repeat = repeat letは再帰
  32. 32. repeat(3){foo}!3 例3:整数値引数をとる let repeat n s = if n <= 0 then {} else s ^ (repeat (n - 1) s) let repeat = repeat foofoofoo! letは再帰 テキスト階層の プログラム引数は ( … ) でくくる
  33. 33. 例3:整数値引数をとる let repeat n s = if n <= 0 then {} else {@s;repeat(n - 1){@s;}} foofoofoo! repeat(3){foo}!3 @s;repeat( ){@s;}
  34. 34. LATEXでは • 型検査(特に引数のチェック) ……原理的に厳しい • “文字列以外” の引数の扱い ……非直観的なマクロ定義が必要 ↔ Macrodown だと簡単(やったぜ。)
  35. 35. 実用的な言語の話: マークアップ 特化機能
  36. 36. 1 軽量リスト記法 div-each{|hoge|piyo|fuga|moge|} <div>hoge</div> <div>piyo</div> <div>fuga</div> <div>moge</div> 文字列リストを処理するとき,いちいち div-each( [{hoge}; {piyo}; {fuga}; {moge}] ); などと書くのは読み書きに不便
  37. 37. 1 軽量リスト記法 div-each{|hoge|piyo|fuga|moge|} <div>hoge</div> <div>piyo</div> <div>fuga</div> <div>moge</div> let div-each [] = {} | (hd :: tl) = {<div>@hd;</div> div-each(tl);} string list 型 <div>@hd;</div> div-each(tl);tl
  38. 38. 2 多重軽量リスト記法 listing{ * 東京 ** 駒場 ** 本郷 ** 弥生 ** 浅野 * 千葉 ** 柏 } <ul> <li>東京 <ul> <li>駒場</li> <li>本郷</li> <li>弥生</li> <li>浅野</li> </ul></li> <li>千葉 <ul> <li>柏</li> </ul></li> </ul>
  39. 39. 2 多重軽量リスト記法 listing{ * 東京 ** 駒場 ** 本郷 ** 弥生 ** 浅野 * 千葉 ** 柏 } ヴァリアント型で実現 itemize = Item of string * itemize list
  40. 40. 2 多重軽量リスト記法 listing{ * 東京 ** 駒場 ** 本郷 ** 弥生 ** 浅野 * 千葉 ** 柏 } ヴァリアント型で実現 Item({}, [ Item({東京}, [ Item({駒場}, []); Item({本郷}, []); Item({弥生}, []); Item({浅野}, []) ]); Item({千葉}, [ Item({柏}, []) ]) ]) itemize = Item of string * itemize list
  41. 41. 2 多重軽量リスト記法 listing{ * 東京 ** 駒場 ** 本郷 ** 弥生 ** 浅野 * 千葉 ** 柏 } ヴァリアント型で実現 Item({}, [ Item({東京}, [ Item({駒場}, []); Item({本郷}, []); Item({弥生}, []); Item({浅野}, []) ]); Item({千葉}, [ Item({柏}, []) ]) ]) itemize = Item of string * itemize list
  42. 42. 2 多重軽量リスト記法 listing{ * 東京 ** 駒場 ** 本郷 ** 弥生 ** 浅野 * 千葉 ** 柏 } ヴァリアント型で実現 Item({}, [ Item({東京}, [ Item({駒場}, []); Item({本郷}, []); Item({弥生}, []); Item({浅野}, []) ]); Item({千葉}, [ Item({柏}, []) ]) ]) itemize = Item of string * itemize list
  43. 43. 2 多重軽量リスト記法 Item({}, [ Item({東京}, [ Item({駒場}, []); Item({本郷}, []); Item({弥生}, []); Item({浅野}, []) ]); Item({千葉}, [ Item({柏}, []) ]) ]) あとはこの値を パターンマッチで 再帰的に出力に変換 するだけ 多重リストが 簡潔に書けてしかも 静的型に取り込めている
  44. 44. div .test #sentence { The quick brown fox jumps over the lazy dog. } 3 クラス名・ID名オプション <div class="test" id="sentence"> The quick brown fox jumps over the lazy dog. </div> 余剰(省略可能)な引数 HTML を強く意識した機能 現在仕様改定を計画中のため,詳細は割愛 (’a maybe 型 の値として扱いたい)
  45. 45. 4 コード記述用領域 code `hoge;@var;` display-C ``` #include <stdio.h> int main(void) { printf("Hello Code Arean"); return 0; } ``` haskell ``a `elem` xs`` “文字通り” に書ける領域
  46. 46. 5 出力時インデント let p content = {<p>deeper{@content;}</p>}<p>deeper{@content;}</p> 出力のインデントを深くし, 前後に改行を補う
  47. 47. 5 出力時インデント let p content = {<p>deeper{@content;}</p>} <p> Time flies like an arrow. </p> p{Time flies like an arrow.} <p>deeper{@content;}</p> deeper が改行とインデントを挿入
  48. 48. p{␣Time flies like an arrow.␣} 5 出力時インデント let p content = {<p>deeper{@content;}</p>} <p> Time flies like an arrow. </p> <p>deeper{@content;}</p> { 直後と } 直前は 空白と改行を無視
  49. 49. 5 出力時インデント let p content = {<p>deeper{@content;}</p>} <p> Time flies like an arrow. </p> p{↲ ␣␣Time flies like an arrow.↲ } <p>deeper{@content;}</p>
  50. 50. 5 出力時インデント <p> Time flies like an arrow. Fruit flies like a banana. </p> p{↲ ␣␣Time flies like an arrow.↲ ␣␣Fruit flies like a banana.↲ } 途中の改行は 無視されない
  51. 51. 6(準備:破壊的代入) let-mutable x <- 1 in (arabic !x) ^ {/} ^ ( x <- 2 before arabic !x ) %=> {1/2}
  52. 52. 6(準備:破壊的代入) let-mutable x <- 1 in (arabic !x) ^ {/} ^ ( x <- 2 before arabic !x ) %=> {1/2} x : int ref
  53. 53. 6(準備:破壊的代入) let-mutable x <- 1 in (arabic !x) ^ {/} ^ ( x <- 2 before arabic !x ) %=> {1/2} 参照
  54. 54. 6(準備:破壊的代入) let-mutable x <- 1 in (arabic !x) ^ {/} ^ ( x <- 2 before arabic !x ) %=> {1/2} 破壊的代入
  55. 55. 6(準備:破壊的代入) let-mutable x <- 1 in (arabic !x) ^ {/} ^ ( x <- 2 before arabic !x ) %=> {1/2} 逐次評価
  56. 56. document{ title{MLstudy} }{ maketitle; … } ここでタイトルを 指定して ここでタイトルの 文字列を出力に使いたい 6 擬似遅延 やや強引な例: タイトル文字列を使い回したい
  57. 57. <!DOCTYPE html> <html> <head> … <title>MLstudy</title> … </head> <body> <div class="title">MLstudy</div> … </body> </html> 6 擬似遅延
  58. 58. let-mutable title <- {} let maketitle = let ttl = !title in {<div class="title">@ttl;</div>} let title ttl = title <- ttl before {<title>@ttl;</title>} document{ title{MLstudy} }{ maketitle; … } <div class="title">@ttl;</div> <title>@ttl;</title> 6 擬似遅延
  59. 59. let-mutable title <- {} let maketitle = let ttl = !title in {<div class="title">@ttl;</div>} let title ttl = title <- ttl before {<title>@ttl;</title>} document{ title{MLstudy} }{ maketitle; … } <div class="title">@ttl;</div> <title>@ttl;</title> 即座に 評価されて 破滅! 6 擬似遅延
  60. 60. let-mutable title <- {} let maketitle () = let ttl = !title in {<div class="title">@ttl;</div>} let title ttl = title <- ttl before {<title>@ttl;</title>} document{ title{MLstudy} }{ maketitle(()); … } <div class="title">@ttl;</div> <title>@ttl;</title> 普通の 回避方法: () でサンク () 6 擬似遅延
  61. 61. let-mutable title <- {} let maketitle () = let ttl = !title in {<div class="title">@ttl;</div>} let title ttl = title <- ttl before {<title>@ttl;</title>} document{ title{MLstudy} }{ maketitle(()); … } <div class="title">@ttl;</div> <title>@ttl;</title> 😫 実装方法に依存した, マークアップ上意味のない () が現れる…… () 6 擬似遅延
  62. 62. 6 擬似遅延 let-lazy let-mutable title <- {} let-lazy maketitle = let ttl = !title in {<div class="title">@ttl;</div>} let title ttl = title <- ttl before {<title>@ttl;</title>} document{ title{MLstudy} }{ maketitle; … } <div class="title">@ttl;</div> <title>@ttl;</title> 😄 エレガントな表記で 意図通り動く。 let-lazy を使うと
  63. 63. 最後に: 用法・容量を守るべき 闇の機能 最終値参照
  64. 64. 最終値参照 簡単に言えば 文書の評価がすべて終わった後に “変数” に最終的に格納されている値を 事前に取り出して使うための機能
  65. 65. 最終値参照 簡単に言えば 文書の評価がすべて終わった後に “変数” に最終的に格納されている値を 事前に取り出して使うための機能 ……タイムトラベルかな? ⇒ 違います
  66. 66. 最終値参照が必要な場面 section {はじめに}{ いろは歌を第ref{iroha}章に掲げる。 } section #iroha {いろは歌}{ 色は匂へど散りぬるを,我が世誰ぞ恒ならむ。 … } 例: 相互参照機能
  67. 67. 最終値参照が必要な場面 section {はじめに}{ いろは歌を第ref{iroha}章に掲げる。 } section #iroha {いろは歌}{ 色は匂へど散りぬるを,我が世誰ぞ恒ならむ。 … } 例: 相互参照機能
  68. 68. 最終値参照が必要な場面 相互参照機能では一般に 参照先が後ろにあるかもしれない しかし章番号を参照したいなら 後ろにある章番号を手前で使う必要がある
  69. 69. 最終値参照が必要な場面 相互参照機能では一般に 参照先が後ろにあるかもしれない しかし章番号を参照したいなら 後ろにある章番号を手前で使う必要がある ちなみに LATEX では こういう場合は複数回タイプセットする Macrodown はそういう力技はやらない主義(?) ⇒ 代わりに邪道を往く
  70. 70. 最終値参照の実現 解決法: 最終値参照用のハッシュテーブルを備えつける
  71. 71. 最終値参照の実現 解決法: 最終値参照用のハッシュテーブルを備えつける • iroha を参照したい箇所には 「ここに iroha に対応する文字列が後で入るよ」 という “印” を書き込む
  72. 72. 最終値参照の実現 解決法: 最終値参照用のハッシュテーブルを備えつける • iroha を参照したい箇所には 「ここに iroha に対応する文字列が後で入るよ」 という “印” を書き込む • iroha の箇所では iroha とそれに対応する文字列を ハッシュテーブルに追加する
  73. 73. 最終値参照の実現 解決法: 最終値参照用のハッシュテーブルを備えつける • iroha を参照したい箇所には 「ここに iroha に対応する文字列が後で入るよ」 という “印” を書き込む • iroha の箇所では iroha とそれに対応する文字列を ハッシュテーブルに追加する • 最後の出力時に “印” をハッシュテーブルをもとに 具体的な文字列で置き換える
  74. 74. 最終値参照 section {はじめに}{ いろは歌を第ref{iroha}章に掲げる。 } section #iroha {いろは歌}{ 色は匂へど散りぬるを,我が世誰ぞ恒ならむ。 … }
  75. 75. section {はじめに}{ いろは歌を第ref{iroha}章に掲げる。 } section #iroha {いろは歌}{ 色は匂へど散りぬるを,我が世誰ぞ恒ならむ。 … } 最終値参照 最終的に iroha に対応する 文字列に置き換える “印” !! {iroha} iroha と対応させる章番号の文字列 {2} を ハッシュテーブルに追加する: new-global-hash {iroha} <<- {2}
  76. 76. 評価終了=出力直前のイメージ <section>deeper{ <h1>第1章 はじめに</h1> <p>deeper{ いろは歌を第(!! {iroha})章に掲げる。 }</p> }</section> <section>deeper{ <h1 id=>第2章 いろは歌</h1> <p>deeper{ 色は匂へど散りぬるを,我が世誰ぞ恒ならむ。 … }</p> }</section> “印” が文字列内に 残っている 評価終了時,ハッシュテーブル内 では iroha と {2} が対応
  77. 77. 出力 <section> ␣␣<h1>第1章 はじめに</h1> ␣␣<p> ␣␣␣␣いろは歌を第2章に掲げる。 ␣␣</p> </section> <section> ␣␣<h1>第2章 いろは歌</h1> ␣␣<p> ␣␣␣␣色は匂へど散りぬるを,我が世誰ぞ恒ならむ。 ␣␣␣␣… ␣␣</p> </section> iroha と {2} の対応が ハッシュテーブルにあるので 書き換えて出力
  78. 78. 最終値参照の闇1 section { いろは歌を第ref{aiueo}章に掲げる。 } section #iroha { 色は匂へど散りぬるを,我が世誰ぞ恒ならむ。 … } 存在しない “印” を 書いてしまっていたら……?
  79. 79. 最終値参照の闇1 section { いろは歌を第ref{aiueo}章に掲げる。 } section #iroha { 色は匂へど散りぬるを,我が世誰ぞ恒ならむ。 … } 存在しない “印” を 書いてしまっていたら……? ! [ERROR AT EVALUATOR] undefined global hash key ’aiueo’. 実行時エラー!(事前にはわからない)
  80. 80. 最終値参照の闇2 最終値参照の結果の文字列を具体的に加工するよ うなプログラムを書くと…… ⇒ 予想がつかないことになりがち(説明は割愛)
  81. 81. 最終値参照の闇2 (string-sub (!! {test}) 0 3) ^ {/} ^ ( new-global-hash {test} <<- {Macrodown} before !! {test} ) 最終値参照の結果の文字列を具体的に加工するよ うなプログラムを書くと…… ⇒ 予想がつかないことになりがち(説明は割愛) 上の例は {Mac/Macrodown} になると 思いがちだが実際にはならない test test test Macrodown /
  82. 82. 最終値参照まとめ • 相互参照機能 • 目次の生成 • 出力される LATEX ファイルが依存する パッケージの列挙 という用途に便利だが,闇の側面もある
  83. 83. HTML PDFLATEXコード Macrodown インタプリタ LATEX エンジン (+ドライバ) LATEX出力用 ライブラリ HTML出力用 ライブラリ 役割としてはプリプロセッサ 実用上の Macrodown 入力
  84. 84. PDFLATEXコード Macrodown → LATEX → PDF Macrodown インタプリタ LATEX エンジン (+ドライバ) 入力 LATEX出力用 ライブラリ 実用上の Macrodown
  85. 85. HTML Macrodown インタプリタ HTML出力用 ライブラリ Macrodown → HTML 実用上の Macrodown 入力
  86. 86. HTML PDFLATEXコード Macrodown インタプリタ LATEX エンジン (+ドライバ) LATEX出力用 ライブラリ HTML出力用 ライブラリ 実用上の Macrodown 入力
  87. 87. 言語 X コード Macrodown インタプリタ 言語 X 出力用 ライブラリ 出力形式に依存せず共通の方式でマークアップ ユーザ自身がライブラリを書いて用意すれば あらゆる形式に変換可能 実用上の Macrodown 入力
  88. 88. まとめ Macrodown は • MLが使える,ラップ用マークアップ言語 • 静的型検査ができる (マークアップと型システムは意外に相性良好) • マークアップ特化機能も充実 - 軽量リスト記法 - インデント出力 - 擬似遅延 - 最終値参照 - クラス・ID名オプション
  89. 89. 今後の発展 • 浮動小数点数やレコード型の実装 • 軽量数式記法(実用上かなり重要) • 軽量テーブル記法 • ライブラリの整備 • 仕様書を書き起こす • 最終値参照の闇を軽減する型システム検討 pure-string <: string deeper ・改行・!! を含まない純粋な文字列
  90. 90. 実装 現在の実装 (ver. 1.00δ) は http://github.com/gfngfn/Macrodown で公開しています(正式リリースはまだ) Pull request 大歓迎!(泣いて喜びます)

×