MarkdownよりrSTを好む理由
(buttondown.email)- SphinxのreStructured Text(rST) はMarkdownより習得が難しいが、書籍のような大規模文書では構造や出力形式を細かく制御しやすい
- Markdownは軽量なHTML記法に近く、rSTは抽象ドキュメントツリーを中心に、ディレクティブ・ノード・レンダラーを組み合わせて新しい文書オブジェクトを追加できる
- Sphinxはレンダリング前にdoctreeを変換するため、相互参照、出力形式ごとの処理、特定のビルド段階での変換といった作業を文書システム内で扱える
- Logic for Programmersでは練習問題と解答を原文の近くに書いたうえで、EPUBとLaTeX出力では位置や表示方法を変えるカスタム拡張を使っている
- シンプルなMarkdownは統一された拡張構文やレンダリング前の変換サポートが不足しており、文書ジェネレーターが別の前処理で回避するほど、ツール支援や拡張性が弱くなる
rSTを選んだ理由
- Logic for Programmersの新バージョンは、Sphinxで書いた2冊目の本であり、前作の新しいLearn TLA+でもSphinxを使っている
- SphinxはreStructured Textを使い、rSTはMarkdownより学習曲線が急である
- Markdownで何冊もの本を書いたあと、より良いツールが必要になってrSTへ移行した
- rST自体はSphinxとは独立しているが、実際にはSphinxのためにrSTを使うことが多いため、ここでは両方を合わせて扱う
MarkdownとrSTの構造の違い
- 最大の違いは、Markdownが軽量なHTML記法に近いのに対し、rSTは抽象ドキュメントツリーを作る中規模の記法だという点である
- Markdownの画像構文は、簡単な変換だけで
<img alt="alttext" src="example.jpg"/>のようなHTMLに変えられる- 最近のMarkdownエンジンも中間表現にパースすることは多いが、基本的な性格は軽いHTML記法に近い
- rSTの画像は
.. image::ディレクティブで表現される- Sphinxは登録済みのディレクティブハンドラーを探し、
ImageDirective.runを実行する - 実行結果は
altフィールドを持つimage_nodeのようなノードオブジェクトになる - doctree全体の処理が終わると、HTML Writerが
image_nodeのレンダリング関数を探してHTMLタグを出力する
- Sphinxは登録済みのディレクティブハンドラーを探し、
- rST方式は実装も構文もより複雑で、Markdownよりボイラープレートも多いが、画像も他のディレクティブと同じ拡張メカニズムで扱える
新しい文書オブジェクトを追加する方法
- rST/Sphinxでは、新しいテキストオブジェクトを拡張として追加できる
- たとえば
<image>の代わりに<figure>と<figcaption>を作りたいなら、標準的なMarkdownではHTMLを直接挿入する必要がある - Sphinxでは、新しい
figureディレクティブを登録する形で対応するFigureDirectiveがImageDirectiveを継承し、画像処理の大半を再利用することもできる
- ディレクティブ登録、ノード生成、ビルダーごとのレンダラー登録というパターンは、あらゆる拡張に同じように適用される
レンダリング前のdoctree変換
- Sphinxはレンダリング前にdoctree変換を実行できる
- 文書間の相互参照もこの機能で処理される
- ある文書に
fooアンカーがあり、別の文書に:ref:\image <foo>``がある場合、Sphinxは後処理段階で正しいURLを挿入する
- ある文書に
- 変換コードはビルドプロセス内の第一級機能のように扱われる
- HTML出力のときだけ特定の変換を適用できる
- 特定のビルド段階で変換を実行できる
- 実行したくない組み込み変換を削除することもできる
- すべての文書にこれほどの強力さが必要なわけではなく、Markdownは軽量で移植性が高いため広く使われている
練習問題と解答の拡張事例
- Logic for Programmersは数学寄りの本なので、読者向けの練習問題が必要である
- 執筆時には練習問題と解答を文書内で近くに置くほうが楽だが、読者には解答が本の後ろに出るべきである
- 要件は出力形式ごとに異なっていた
- 練習問題と解答を相互にリンクする必要がある
- 印刷を考慮して、PDFにはページ参照も必要である
- LaTeX/PDF出力とEPUB出力ではレンダリング方法を変える必要がある
- そのため
exercise、solution、solutionlistを処理するカスタムSphinx拡張を書いた - HTMLのデバッグ出力では、練習問題と解答をインラインでレンダリングする
- EPUBとLaTeXの生成では、doctree全体を作ったあとに変換を実行する
- 元の位置にあったすべての
solution_nodeをsolutionlistの下へ移動する - 各練習問題には新しい解答位置へ移動する参照ノードを付ける
- 各解答には元の練習問題へ戻る参照ノードを付ける
- 元の位置にあったすべての
- LaTeXビルダーは練習問題と解答をanswers environmentで囲む
- EPUBビルダーは解答をpopup footnoteとしてレンダリングする
- この構造は本の無料サンプルを作るときにも役立つ
- 無料サンプルの末尾には本全体の解答ではなく、サンプルに含まれる部分の解答だけを入れる
構文の好みと代替案
- rSTに対する最も一般的な反対意見は、構文が醜いという点である
- ツールの見た目が好きになれず使わないというのも十分にありうる選択であり、Lispを受け入れにくい理由も同じく好みの問題と見なせる
- 代替としてasciidoc、MyST、Typst、Pollen、pandoc-extended markdownがある
- 要点は、Sphinx/rSTが大規模文書化に例外的に優れているということではなく、シンプルなMarkdownが大規模文書化に例外的に向いていないという点にある
Markdownベース生成器の限界
- シンプルなMarkdownには、統一された拡張構文やレンダリング前変換のネイティブサポートがない
- 多くのMarkdownベース文書ジェネレーターは、新しいユースケースを支えるために独自の前処理段階を付け加えている
- この方式はたいてい動作するが、Markdownの中で処理するのではなく、Markdownの外側で回避する構造になる
- その結果、機能の強力さには限界が生じ、プログラマー向けツールもその変形をうまく理解しにくい
- Markdown用とrST用のLSPやtreesitterはあるが、gitbook-markdown、md-markdown、leanpub-markdown向けに同水準のツールを期待するのは難しい
- rSTの醜い構文は、むしろ構文木が豊かだという利点として働くことがある
- 特定の
todoディレクティブの本文だけを書き換えるtreesitterクエリが可能である - これはrSTの構文木がMarkdownの構文木より豊かだから可能になっている
- 特定の
Logic for Programmersアップデート
- Logic for Programmersは、形式論理が日常的なソフトウェアエンジニアリングにどう役立つかを扱う本である
- 本は基本的な数学の概説から始まり、プロパティテスト、データベース制約、決定表など8つの応用へと続く
- まだアルファ段階だが、2万語規模で、読者からのフィードバックを募っている
1件のコメント
Hacker Newsのコメント
「見ただけで吐きそうだから良いツールを使わないのか」と聞かれたら、そうだと答える。Markdownの最大の利点は読みやすいことで、2つ目の利点は書きやすいことにある。
パースがどれだけ簡単か、拡張がどれだけ簡単かはほとんど重要ではない。本を書くのにMarkdownが最善かどうかはさておき、構文をよく知らない人でも読みやすい形で、素早く書式付きの文章を書く用途にはMarkdownが最高だ。本を書こうとしているのではなく、メモ、素早いドキュメント化、コメント投稿が必要なだけで、本を書くならRSTより先にLaTeXを使うと思う。
ところが実際のアプリで使ってみると、Markdownの核心はそこではなかった。最小限の書式だけを提供し、プレーンテキストの状態でもHTMLにレンダリングされた状態と同じくらい自然に読めることが目的なのだ。サポートする書式が意図的に少ないので頭に入りやすく、ツールバーなしで使える。コメント入力欄、チャット、コミットメッセージ、あるいはブログ記事には合っているが、エンタープライズ級の製品ドキュメント作成には向かない。最近はHTMLにレンダリングされない場所でもMarkdownを使うが、それ自体が読みやすいからで、HNでも対応してほしい。
技術文書もかなり多くMarkdownで作ってきたし、Pandoc拡張https://pandoc.org/MANUAL.htmlを使えば、複雑な数式やシンタックスハイライト付きコードブロックを含め、必要な書式はほぼすべて入れられる。そのMarkdownはHTML、Word文書、ePub、PDFなどに変換できる。Markdown以外のものを持ち出すには、かなり説得力のある理由が必要だ。
TeXで見た最大の問題は言語ではなく人の問題だ。人々はひどいスタイルのスパゲッティTeXをよく書く。しかし「文書はコード」という考え方で書けば、かなりきれいな結果になる。2番目に大きな問題は、良いTeX→HTMLコンパイラがないことだ。
LaTeXに熟達しているわけではないが、一度学ぼうとしたときは昆虫型の異星文明の言語を学んでいるような感覚だった。まったく直感的ではなく、他人がすでにやっておいたものをコピーして自分の文章だけ差し込むやり方でなければ、新しいことをするのはほとんど不可能だった。記憶では、一級のUnicodeサポートもなかった。
イタリックにアスタリスクやアンダースコアを使うのも慣れが必要で、
/italic slashes/のようなはるかに直感的な方法がある。基本を外れると、表やメタデータ、タグがテキストを隠してしまい、適切なツールなしでは書くのも読むのも簡単ではない。拡張が簡単ならこうした基本的な問題も直せるので、拡張性も関係している。技術ドキュメントライターとして約12年働いており、キャリア初期にはスタートアップのドキュメントを Word から Sphinx に移行した。その後、Google の独自 CMS/開発者ドキュメントプラットフォーム、Eleventy ベースのサイト、ここ2年は再び Sphinx ベースのサイトである pigweed.dev に携わっている。readme.com ベースのスタートアップ案件も経験し、Docusaurus、Astro、Hugo も少し触ったことがある
reStructuredText 単体では粗いところもあるが、Sphinx と組み合わせた reSTは非常に良い。Sphinx の強みは reST の弱点をはるかに上回る。100ページ以上、コントリビューター10人以上の大規模な専門ドキュメントサイトなら、長期的には Sphinx が最も責任ある選択だとかなり強く考えている。たとえば Pigweed では
:bug:\59385981`` と書くだけで https://pwbug.dev/59385981 へのリンクに変換されるようにしており、あとでバグリンクを大量に移行する必要が出ても簡単だ。内部リンクも常に解決されることが保証され、存在しない場所にリンクすると警告やエラーが出る。これがドキュメントサイトの標準ではないのはおかしい、と以前 https://technicalwriting.dev/src/link-text-automation.html に書いた。Sphinx は拡張機能とテーマ API もよく定義されており、PyPI 上のエコシステムもかなり大きい。最近は Sphinx をドキュメントシステムの眠れる巨人と呼んでいて、少し力を集めるだけではるかにすごいものになり得るスラッグが変わったりサイト構造を再編したりすると、サイト全体で検索・置換しなければならない。静的サイトジェネレーターなら
[Hello](../hello.md)のようにリンクさせ、ビルド時に解決できるはずなのに、よく使われている、あるいは調べたツールの多くは[Hello](/why/hello/)を直接打たせる。この機能は好みが分かれるらしい。静的サイトジェネレーターのチームメンバーに話しても「なぜそれが欲しいのか」という答えが返ってきて、説明しても通じなかった。問題を経験して初めて解決策の価値が分かるのか、一度書いて10年以上保守しないことに慣れているのか分からないが、もっと広くサポートされるとよいプラグインエコシステムが素晴らしく、チームやプロジェクトのドキュメントを改善するうえで非常に大きなレバレッジになる。reStructuredText 自体は好きではないが、最近は MyST-Parser のおかげで、以前は Sphinx が RST に強く縛られていた作業の大半を Markdown でもできるようになっている: https://github.com/executablebooks/MyST-Parser
内部言語/VM/抽象化レイヤーを説明する200ページ超の本を Sphinx に移したばかりだが、本当に人生を変えるシステムだ。Sphinx 自体のドキュメントの参入障壁がもっと低いか、例がもっと多ければと思うが、今はかなり強い新婚期のような感覚だ。主な関心は、見栄えのよい PDF 本を作る方法と、本を章・節単位で POSIX 互換の man ページに切り出すシステムだ
サイトジェネレーターを選ぶとき、美観はかなり重要な要素だ。Hugo と Gatsby はデフォルトテーマが素晴らしく、実際にその理由だけでプロジェクトに選んだこともある。Sphinx のテーマ集 https://sphinx-themes.org/ と https://sphinxthemes.com/#featured-themes は総じて地味だ。標準の Sphinx RTD テーマ https://sphinx-rtd-theme.readthedocs.io/en/stable/ を Apple のドキュメント https://developer.apple.com/documentation/swift/array や Fluent UI https://react.fluentui.dev/?path=/docs/concepts-developer-positioning-components--default と比べると古く見える
「MarkdownはHTMLの軽量表現」という文が、この記事で最大の問題だと思う。それは明らかに不正確
Markdownは、1990年代初頭のメールやUsenetの投稿で事実上の標準のように使われていたテキスト整形の慣習を変換するためのツールとして設計された。7ビットASCIIという制約のため、強調や見出しのような書式を特殊記号で示すようになり、HTMLもその名前のない慣習と似ている点が多かった。そこでJohn Gruberが2004年に、それをHTMLへ変換する基本的なスクリプト https://daringfireball.net/projects/markdown/ を書いたが、これほど広く使われる実質的な標準になるとは予想していなかったはず
GruberはUsenetの事実上の標準を持ってきて単にHTMLコンバーターを作ったのではなく、Usenetやほかの慣習から借用しつつ、自分独自のマークアップを設計した。リンク下部の “Acknowledgements” もその事実を示している。Markdownは最初からWeb CMS用のマークアップ構文として意図されており、HTMLの軽量表現と言うのは正しい。構文のあらゆる部分が、直接対応するHTMLを生成するようにすることが核心だった
メールの慣習に触発されたという事実は、「MarkdownはHTMLの軽量表現」という言い方の正しさを弱めるものではない
相手の発言について最ももっともらしく強い解釈に答え、批判しやすい弱い解釈を取り上げない、というルールがある。記事の中で最も挑発的な一文だけを拾って文句を言うのではなく、興味深い部分に応答せよというルールもある: https://news.ycombinator.com/newsguidelines.html
記事の核心に同意しないなら、rSTよりMarkdownを好むと言い、その理由を説明すればよい。Markdownが正確には何かという一文だけをめぐって争うのは愚かだ
メールやUsenetのような慣習に触発されてはおり、その一部はコンピューター以前にも存在した。たとえば古いタイプライター文書で、アスタリスクをイタリックのように使っている例を見たことがある気がする。しかしMarkdownはHTMLと強く結び付いており、構文もHTMLに大きく制約されている。HTMLから切り離そうとする試みは、たいてい失敗せざるを得ない
Markdownの核心は、生のHTMLより簡単なことをより速くできるようにしつつ、必要なら生のHTMLを混ぜられるようにする点にあると思う
MarkdownよりRSTの力が必要だったプロジェクトでは、むしろHTMLを直接書くほうが楽だった
似た複雑さの文書システムを作る中で、RSTファイルの構造をデータベースに保存し、データベースの結果をコンテンツと混ぜるような、明確な意味を持つマークアップが切実に必要だったためRSTを検討した
ぶつかった問題は2つあった。第一に、RSTツールにはRSTを再出力するアンパーサーがない。複数のRSTファイルや他のソースをマージしてRSTファイルを自動生成し、文書APIとして扱いたかったが、サポートされていなかった。第二に、RSTツールは特定の文書について定義されたブロック集合を期待する。ブロックを一般的に表現できれば、内部のブロック定義を知らなくても文書を変換するツールが可能になるはずだが、そうなっていなかった。これはRST自体というよりツールの問題だが、コードを下層まで掘り返さなければならないたびに、HTMLベースのような別のマークアップシステムを考えたくなる
この方式の利点は入力スキーマと出力を完全に制御できること、欠点はMarkdownやRSTより構文上のノイズがはるかに大きく、望む出力形式へパース・変換するスクリプトが必要になること
docutilsの全体的な目的は、形式をパースしてAPIへ変換することにある: https://www.docutils.org/docs/index.html#api-reference-material-for-client-developers
数年前、覚えておく価値のある reStructuredText のサブセットをまとめたことがあります: https://simonwillison.net/2018/Aug/25/restructuredtext/
最近のプロジェクトでは MyST を使い始めました。reStructuredText で重視していた参照や目次機能を提供しつつ、コントリビューターが書きやすい Markdown 記法を使えます。
本当に状況を変えるのは、内部リンクにおける rST+Sphinx と
:ref:、:doc:ディレクティブです。同じコンテンツ内でアンカーやドキュメントリンクを参照するときにヘッダーを直接入力しなくてよく、直接入力したヘッダーがいずれ古くなってしまうことを避けられます: https://www.sphinx-doc.org/en/master/usage/referencing.html#ref-rolerST で文章を書くときに最も恋しくなる機能の一つです。
ReStructuredText の話に割り込むつもりではありませんが、Markdown より多くのことができるマークアップ言語を探しているなら、ReStructuredText より AsciiDoc を見ることを勧めたいです。3つすべてで何年も技術文書を書いてきましたが、AsciiDoc は ReStructuredText と Markdown より優れていると思います。
たとえば Markdown と ReStructuredText の表サポートは非常に面倒です。AsciiDoc の表書式は読み書きや保守がしやすく、ヘッダー、キャプション、表・行のカスタムサイズ、表内の複雑な書式までサポートしていて、より強力です。Markdown のように複数の方言がない単一の標準形式で、構文は簡潔で読みやすく、ReStructuredText より学習曲線が緩やかです。出力スタイリングの選択肢も良く、ツールチェーンも優秀で、組み込みのドキュメント機能が豊富なため、サードパーティープラグインに頼ることが少なくて済みます。AsciiDoc は最初から技術文書向けに設計されており、ほかの2つはその役割に合わせ込まれた側です。
5〜10ページ程度の Markdown ドキュメントをきれいに構成し、それ自体はより動的な Jinja テンプレートでレンダリングされるようにしておくと、かなり満足のいく形で始められます。自動ドキュメントのためのビルドプロセスもあり、単一の GitHub README では大きすぎる程度です。ところが、そこから苦痛が始まります。
GitHub プロジェクトページのドキュメントはうまく合わず、
.nojekylファイルが必要なのか、gh-pagesブランチがまだ必要なのかで混乱します。リポジトリ設定のミスなのか変更が反映されていないのか分からず、GitHub Actions を試して数時間が過ぎるころには不合理な状態になります。Read the Docs を改めて見ると Sphinx を求めているようなので Markdown と Sphinx をつなぎ、ビルドは通るものの、デプロイ後にページ幅が崩れ、ローカルでは再現しないため、コミュニティティアの広告挿入のせいらしく見えます。多くのプロジェクトでうまく動いていますし、自分でもやったことはありますが、動くようになるまでは信じられないほど細々と面倒です。結局、Markdown 対 RST は関心事ではなく、中規模のドキュメントプロジェクトと静的ホスティングにうまく合う組み合わせを見つけることが核心です。自動デプロイの案内もよく整っています: https://github.com/rust-lang/mdBook
著者が自分の本の組版という文脈で話している点を見落としているように思います。一般に rST が Markdown より優れていると主張しているわけではありません。
一般的な場合には Markdown の単純さが広く使われる理由ですが、著者が話している対象はそれではありません。
reST が Markdown の競合として作られたかのように反応されるのは面白いです。実際にはほぼ逆です。reST は 2002年の StructuredText の発展形で、Markdown は 2004年に初めて公開されました。
両者の目標は非常によく似ており、最も基本的なテキストではどちらもプレーンテキストのように読んだり書いたりできます。その時期に皆がこういうものを求めるようになり、複数の形式が登場したのです。Markdown が勝った理由は「より単純」だとか「より読みやすい」という点とはあまり関係がないと思います。純粋な ASCII と空白で簡単に表せる内容では、たいてい相互に置き換えられます。例にある reST 文書を、パーサーなしでは読めない難解な文章だと言う人がいるでしょうか。Markdown の変種がこれよりどの点で優れているのかはよく分かりません。歴史的な偶然に近い形で一方が優勢になっただけで、どちらも中核的な目標には十分に適しています。
reST は必要な場合には有用な追加書式機能を多く提供しますが、不要なときには余計なものです。GitHub に登録した2010年ごろに GitHub-flavored Markdown を使い始め、Python ドキュメントのために reStructuredText も何度か使いました。後者は学習曲線がはるかに高く、その後は使う理由がありませんでした。
二重バッククォートも、実際にかかる時間に比べて過剰に苛立たしい構文です。