macrodown -mlが使えるml-

90
-ML が使える ML- 東京大学大学院 情報理工学系研究科 T. SUWA @bd_gfngfn gfngfn Meta Language Markup Language gfn

Upload: t-suwa

Post on 16-Apr-2017

2.487 views

Category:

Engineering


0 download

TRANSCRIPT

-ML が使える ML-

東京大学大学院

情報理工学系研究科

T. SUWA @bd_gfngfn gfngfn

Meta Language Markup Language

gfn

突然ですが,

軽量マークアップ言語 をつくりました。

突然ですが,

軽量マークアップ言語 をつくりました。

「……どうせMarkdown方言じゃないの?」

突然ですが,

軽量マークアップ言語 をつくりました。

「……どうせMarkdown方言じゃないの?」

――当たらずとも遠からず。

突然ですが,

軽量マークアップ言語 をつくりました。

「……どうせMarkdown方言じゃないの?」

――当たらずとも遠からず。

その名も

Macrodown

突然ですが,

軽量マークアップ言語 をつくりました。

「……どうせMarkdown方言じゃないの?」

――当たらずとも遠からず。

その名も

-ML が使える ML- Meta Language Markup Language

@bd_gfngfn gfngfn

gfn 東京大学大学院

情報理工学系研究科

T. SUWA

-ML が使える ML- Meta Language Markup Language

@bd_gfngfn gfngfn

gfn 東京大学大学院

情報理工学系研究科

T. SUWA

なぜ

をマークアップに?

背景にある問題意識

TEX言語(とその上の LATEX)

背景にある問題意識

TEX言語(とその上の LATEX)

激ヤバ プログラミング言語

つよい ライブラリ

(あくまでも極端な解釈です) (語弊が大きい)

背景にある問題意識

TEX言語(とその上の LATEX)

😄 ユーザが柔軟にマクロ定義できる

😫 構文も意味論も闇

• 構文木という概念がない

• 字句解析と構文解析と評価が同時進行

• “合流性” なし

• エラーの原因がわかりづらすぎる

解決案

⇒ 普通のプログラミング言語にしよう!

⇒ 静的型検査の力を借りよう!

それなら……

しかない。

ML系の構文と意味論が

マクロ定義に使える軽量マークアップ言語

ここでの「マクロ」はマークアップ言語的用法

マクロ≒函数

(defmacro …) 的な意味とは異なる

Macrodown の概要

基礎的なML系の性質を備える

• 値呼び戦略

• 多相型推論(Hindley—Milner多相)

• 代数的データ型

• パターンマッチ

• モジュール(“非第1級”)

Macrodown の概要

出力 Macrodown インタプリタ

文書本体 (.mcrd)

基本的には文書用のマクロ集を文書と別に書く

文書用マクロ集 (.mcrdh)

マクロ定義の方法

出力 Macrodown インタプリタ

文書本体 (.mcrd)

基本的には文書用のマクロ集を文書と別に書く

文書用マクロ集 (.mcrdh)

マクロ定義の方法

ML風の構文 で書ける

LATEX+Markdown 風の 構文で書ける

• プログラム階層

- 主にマクロ定義に使う

- マクロ集 (.mcrdh ) ではこの階層が主体

- ML 風の構文

• テキスト階層

- 主にマークアップに使う

- 文書本体 (.mcrd ) ではこの階層が主体

- LATEX+Markdown 風の構文

構文の “階層”

文書本体 main.mcrd

文書用マクロ集 first.mcrdh

例1

Hello, \mcrd;!

let \mcrd = {Macrodown} Macrodown

文書本体 main.mcrd

文書用マクロ集 first.mcrdh

例1

Hello, \mcrd;!

let \mcrd = {Macrodown} Macrodown

プログラム階層

テキスト階層

文書本体 main.mcrd

文書用マクロ集 first.mcrdh

例1

Hello, \mcrd;!

let \mcrd = {Macrodown} Macrodown

プログラム階層

テキスト階層

入れ子の テキスト階層

文書本体 main.mcrd

文書用マクロ集 first.mcrdh

例1

$ macrodown first.mcrdh main.mcrd –o output.txt

Hello, \mcrd;!

let \mcrd = {Macrodown} Macrodown

出力

output.txt

文書本体 main.mcrd

文書用マクロ集 first.mcrdh

例1

Hello, \mcrd;!

Hello, Macrodown!

$ macrodown first.mcrdh main.mcrd –o output.txt

let \mcrd = {Macrodown} Macrodown

例2:文字列引数をとる

let \md s t = {Ma} ^ s ^ {r} ^ t ^ {down}

Hello, Macrodown! Good bye, Markdown!

文字列結合

Ma down r

Hello, \md{c}{o}! Good bye, \md{}{k}!

例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

テキスト階層内変数参照

ここまでは

TEX/LATEX でも簡単なこと

裏を返せば

TEX/LATEX ではここまでしか

簡潔に実装できない

例2:文字列引数をとる

let \md s t = {Ma@s;r@t;down}

Hello, \md{c}!

Ma@s;r@t;down

型が合わないと……

例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

例3:整数値引数をとる

let repeat n s = if n <= 0 then {} else s ^ (repeat (n - 1) s) let \repeat = repeat

letは再帰

\repeat(3){foo}! 3

例3:整数値引数をとる

let repeat n s = if n <= 0 then {} else s ^ (repeat (n - 1) s) let \repeat = repeat

foofoofoo!

letは再帰

テキスト階層の プログラム引数は

( … ) でくくる

例3:整数値引数をとる

let \repeat n s = if n <= 0 then {} else {@s;\repeat(n - 1){@s;}}

foofoofoo!

\repeat(3){foo}! 3

@s;\repeat( ){@s;}

LATEXでは

• 型検査(特に引数のチェック)

……原理的に厳しい

• “文字列以外” の引数の扱い

……非直観的なマクロ定義が必要

↔ Macrodown だと簡単(やったぜ。)

実用的な言語の話:

マークアップ

特化機能

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}] );

などと書くのは読み書きに不便

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

2 多重軽量リスト記法

\listing{ * 東京

** 駒場

** 本郷

** 弥生

** 浅野

* 千葉

** 柏

}

<ul> <li>東京

<ul> <li>駒場</li> <li>本郷</li> <li>弥生</li> <li>浅野</li> </ul></li> <li>千葉

<ul> <li>柏</li> </ul></li> </ul>

2 多重軽量リスト記法

\listing{ * 東京

** 駒場

** 本郷

** 弥生

** 浅野

* 千葉

** 柏

}

ヴァリアント型で実現

itemize = Item of string * itemize list

2 多重軽量リスト記法

\listing{ * 東京

** 駒場

** 本郷

** 弥生

** 浅野

* 千葉

** 柏

}

ヴァリアント型で実現

Item({}, [ Item({東京}, [ Item({駒場}, []); Item({本郷}, []); Item({弥生}, []); Item({浅野}, []) ]); Item({千葉}, [ Item({柏}, []) ]) ])

itemize = Item of string * itemize list

2 多重軽量リスト記法

\listing{ * 東京

** 駒場

** 本郷

** 弥生

** 浅野

* 千葉

** 柏

}

ヴァリアント型で実現

Item({}, [ Item({東京}, [ Item({駒場}, []); Item({本郷}, []); Item({弥生}, []); Item({浅野}, []) ]); Item({千葉}, [ Item({柏}, []) ]) ])

itemize = Item of string * itemize list

2 多重軽量リスト記法

\listing{ * 東京

** 駒場

** 本郷

** 弥生

** 浅野

* 千葉

** 柏

}

ヴァリアント型で実現

Item({}, [ Item({東京}, [ Item({駒場}, []); Item({本郷}, []); Item({弥生}, []); Item({浅野}, []) ]); Item({千葉}, [ Item({柏}, []) ]) ])

itemize = Item of string * itemize list

2 多重軽量リスト記法

Item({}, [ Item({東京}, [ Item({駒場}, []); Item({本郷}, []); Item({弥生}, []); Item({浅野}, []) ]); Item({千葉}, [ Item({柏}, []) ]) ])

あとはこの値を

パターンマッチで

再帰的に出力に変換

するだけ

多重リストが

簡潔に書けてしかも

静的型に取り込めている

\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 型 の値として扱いたい)

4 コード記述用領域

\code `\hoge;@var;` \display-C ``` #include <stdio.h> int main(void) { printf("Hello Code Area\n"); return 0; } ``` \haskell ``a `elem` xs``

“文字通り” に書ける領域

5 出力時インデント

let \p content = {<p>\deeper{@content;}</p>} <p>\deeper{@content;}</p>

出力のインデントを深くし, 前後に改行を補う

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 が改行とインデントを挿入

\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>

{ 直後と } 直前は 空白と改行を無視

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>

5 出力時インデント

<p> Time flies like an arrow. Fruit flies like a banana. </p>

\p{↲ ␣␣Time flies like an arrow.↲ ␣␣Fruit flies like a banana.↲ }

途中の改行は 無視されない

6(準備:破壊的代入)

let-mutable x <- 1 in (arabic !x) ^ {/} ^ ( x <- 2 before arabic !x ) %=> {1/2}

6(準備:破壊的代入)

let-mutable x <- 1 in (arabic !x) ^ {/} ^ ( x <- 2 before arabic !x ) %=> {1/2}

x : int ref

6(準備:破壊的代入)

let-mutable x <- 1 in (arabic !x) ^ {/} ^ ( x <- 2 before arabic !x ) %=> {1/2}

参照

6(準備:破壊的代入)

let-mutable x <- 1 in (arabic !x) ^ {/} ^ ( x <- 2 before arabic !x ) %=> {1/2}

破壊的代入

6(準備:破壊的代入)

let-mutable x <- 1 in (arabic !x) ^ {/} ^ ( x <- 2 before arabic !x ) %=> {1/2}

逐次評価

\document{ \title{MLstudy} }{ \maketitle; … }

ここでタイトルを 指定して

ここでタイトルの 文字列を出力に使いたい

6 擬似遅延

やや強引な例:

タイトル文字列を使い回したい

<!DOCTYPE html> <html> <head> … <title>MLstudy</title> … </head> <body> <div class="title">MLstudy</div> … </body> </html>

6 擬似遅延

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 擬似遅延

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 擬似遅延

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 擬似遅延

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 擬似遅延

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 を使うと

最後に:

用法・容量を守るべき

闇の機能

最終値参照

最終値参照

簡単に言えば

文書の評価がすべて終わった後に

“変数” に最終的に格納されている値を

事前に取り出して使うための機能

最終値参照

簡単に言えば

文書の評価がすべて終わった後に

“変数” に最終的に格納されている値を

事前に取り出して使うための機能

……タイムトラベルかな?

⇒ 違います

最終値参照が必要な場面

\section {はじめに}{ いろは歌を第\ref{iroha}章に掲げる。

} \section #iroha {いろは歌}{ 色は匂へど散りぬるを,我が世誰ぞ恒ならむ。 …

}

例: 相互参照機能

最終値参照が必要な場面

\section {はじめに}{ いろは歌を第\ref{iroha}章に掲げる。

} \section #iroha {いろは歌}{ 色は匂へど散りぬるを,我が世誰ぞ恒ならむ。 …

}

例: 相互参照機能

最終値参照が必要な場面

相互参照機能では一般に

参照先が後ろにあるかもしれない

しかし章番号を参照したいなら

後ろにある章番号を手前で使う必要がある

最終値参照が必要な場面

相互参照機能では一般に

参照先が後ろにあるかもしれない

しかし章番号を参照したいなら

後ろにある章番号を手前で使う必要がある

ちなみに LATEX では

こういう場合は複数回タイプセットする

Macrodown はそういう力技はやらない主義(?)

⇒ 代わりに邪道を往く

最終値参照の実現

解決法:

最終値参照用のハッシュテーブルを備えつける

最終値参照の実現

解決法:

最終値参照用のハッシュテーブルを備えつける

• iroha を参照したい箇所には

「ここに iroha に対応する文字列が後で入るよ」

という “印” を書き込む

最終値参照の実現

解決法:

最終値参照用のハッシュテーブルを備えつける

• iroha を参照したい箇所には

「ここに iroha に対応する文字列が後で入るよ」

という “印” を書き込む

• iroha の箇所では iroha とそれに対応する文字列を

ハッシュテーブルに追加する

最終値参照の実現

解決法:

最終値参照用のハッシュテーブルを備えつける

• iroha を参照したい箇所には

「ここに iroha に対応する文字列が後で入るよ」

という “印” を書き込む

• iroha の箇所では iroha とそれに対応する文字列を

ハッシュテーブルに追加する

• 最後の出力時に “印” をハッシュテーブルをもとに

具体的な文字列で置き換える

最終値参照

\section {はじめに}{ いろは歌を第\ref{iroha}章に掲げる。

} \section #iroha {いろは歌}{ 色は匂へど散りぬるを,我が世誰ぞ恒ならむ。 …

}

\section {はじめに}{ いろは歌を第\ref{iroha}章に掲げる。

} \section #iroha {いろは歌}{ 色は匂へど散りぬるを,我が世誰ぞ恒ならむ。 …

}

最終値参照

最終的に iroha に対応する 文字列に置き換える “印”

!! {iroha}

iroha と対応させる章番号の文字列 {2} を ハッシュテーブルに追加する:

new-global-hash {iroha} <<- {2}

評価終了=出力直前のイメージ

<section>\deeper{ <h1>第1章 はじめに</h1> <p>\deeper{ いろは歌を第(!! {iroha})章に掲げる。

}</p> }</section> <section>\deeper{ <h1 id=>第2章 いろは歌</h1> <p>\deeper{ 色は匂へど散りぬるを,我が世誰ぞ恒ならむ。 … }</p>

}</section>

“印” が文字列内に 残っている

評価終了時,ハッシュテーブル内

では iroha と {2} が対応

出力

<section> ␣␣<h1>第1章 はじめに</h1> ␣␣<p> ␣␣␣␣いろは歌を第2章に掲げる。

␣␣</p> </section> <section> ␣␣<h1>第2章 いろは歌</h1> ␣␣<p> ␣␣␣␣色は匂へど散りぬるを,我が世誰ぞ恒ならむ。 ␣␣␣␣… ␣␣</p>

</section>

iroha と {2} の対応が ハッシュテーブルにあるので 書き換えて出力

最終値参照の闇1

\section { いろは歌を第\ref{aiueo}章に掲げる。

} \section #iroha { 色は匂へど散りぬるを,我が世誰ぞ恒ならむ。 …

}

存在しない “印” を 書いてしまっていたら……?

最終値参照の闇1

\section { いろは歌を第\ref{aiueo}章に掲げる。

} \section #iroha { 色は匂へど散りぬるを,我が世誰ぞ恒ならむ。 …

}

存在しない “印” を 書いてしまっていたら……?

! [ERROR AT EVALUATOR] undefined global hash key ’aiueo’.

実行時エラー!(事前にはわからない)

最終値参照の闇2

最終値参照の結果の文字列を具体的に加工するようなプログラムを書くと……

⇒ 予想がつかないことになりがち(説明は割愛)

最終値参照の闇2

(string-sub (!! {test}) 0 3) ^ {/} ^ ( new-global-hash {test} <<- {Macrodown} before !! {test} )

最終値参照の結果の文字列を具体的に加工するようなプログラムを書くと……

⇒ 予想がつかないことになりがち(説明は割愛)

上の例は {Mac/Macrodown} になると

思いがちだが実際にはならない

test

test test Macrodown

/

最終値参照まとめ

• 相互参照機能

• 目次の生成

• 出力される LATEX ファイルが依存する

パッケージの列挙

という用途に便利だが,闇の側面もある

HTML

PDF LATEXコード

Macrodown インタプリタ

LATEX エンジン

(+ドライバ) LATEX出力用

ライブラリ

HTML出力用 ライブラリ

役割としてはプリプロセッサ

実用上の Macrodown

入力

PDF LATEXコード

Macrodown → LATEX → PDF

Macrodown インタプリタ

LATEX エンジン

(+ドライバ)

入力

LATEX出力用

ライブラリ

実用上の Macrodown

HTML Macrodown インタプリタ

HTML出力用 ライブラリ

Macrodown → HTML

実用上の Macrodown

入力

HTML

PDF LATEXコード

Macrodown インタプリタ

LATEX エンジン

(+ドライバ) LATEX出力用

ライブラリ

HTML出力用 ライブラリ

実用上の Macrodown

入力

言語 X コード

Macrodown インタプリタ

言語 X 出力用 ライブラリ

出力形式に依存せず共通の方式でマークアップ

ユーザ自身がライブラリを書いて用意すれば

あらゆる形式に変換可能

実用上の Macrodown

入力

まとめ

Macrodown は

• MLが使える,ラップ用マークアップ言語

• 静的型検査ができる

(マークアップと型システムは意外に相性良好)

• マークアップ特化機能も充実

- 軽量リスト記法 - インデント出力

- 擬似遅延 - 最終値参照

- クラス・ID名オプション

今後の発展

• 浮動小数点数やレコード型の実装

• 軽量数式記法(実用上かなり重要)

• 軽量テーブル記法

• ライブラリの整備

• 仕様書を書き起こす

• 最終値参照の闇を軽減する型システム検討

pure-string <: string

\deeper ・改行・!! を含まない純粋な文字列

実装

現在の実装 (ver. 1.00δ) は

http://github.com/gfngfn/Macrodown

で公開しています(正式リリースはまだ)

Pull request 大歓迎!(泣いて喜びます)