golden-luckyの日記

ツイッターより長くなるやつ

Haskell 解説本 小史

日本語圏におけるHaskellの解説本には、これまで4回の波がありました。 それを思い出しながら、最後に『プログラミングHaskell 第2版』の紹介をします。

f:id:golden-lucky:20190802181136p:plain

第1波

Haskell解説本の1つめの波は、2006年、『入門Haskell』と『ふつうのHaskell』が出版された頃にありました。 このうち、『入門Haskell』は(おそらく)日本初のHaskell本です。

『入門Haskell』(2006年) 『ふつうのHaskell』(2006年)

『ふつうのHaskell』は、書名だけを見ると「特殊な言語」であるHaskellを「ふつう」に説明している本であるように思えるのですが、実はそうでもなくて、淡々と部品の説明をしていく感じの内容です。 そのぶん例題はあまりなくて、最後にWikiエンジンをがっと作ってみるという構成でした(当時はWikiエンジンを作るのが流行っていたのです)。

一方、『入門Haskell』のほうは、冒頭でまず wc コマンドを実装してHaskellプログラミングの全体像をみせたり、最後にはちょっとしたゲームを開発したり、わりと教育的な内容の本でした。 Haskellを多少は使えるようになった今になって考えると、こういう構成で説明できること自体が、とってもHaskellらしさを伝えている本だったなと思います。ぼくはこの本でHaskellに最初にふれました。

第2波

2つめの波は、2009年ごろ、『Real World Haskell』と『プログラミングHaskell』が出版されたタイミングでしょう。

『Real World Haskell』(2009年) 『プログラミングHaskell』(2009年)

『Real World Haskell』は、「実戦」というキャッチコピーが示すように、仕事や実務でHaskellを使うための情報が詰め込まれた本です。 画像解析をしたり、FFI経由で正規表現のCライブラリを使ったり(当時のHaskellには十分な正規表現のサポートがなかった)、ParsecでCSVのパーザを作ったり、ネットワークプログラミングをしたり、分散並行プログラミングをしたりします。 全部は読んでなかったんですが、いま目次を見たところ、STMの話とかもすでに載ってたらしいので、あらためて読み返してみたくなりました。

一方の『プログラミングHaskell』は、『Real World Haskell』の対局にあるような本で、Haskellを通して「プログラミングそのもの」が学べます。 「プログラミングそのもの」ってなんだよって話ですが、この本で学ぶのは、いわゆる関数プログラミングというやつです。 「(変数への代入ではなく)関数の組み合わせで高度なプログラムの全体を構築していく方法をHaskellというプログラミング言語で解説していく」ことが、『プログラミングHaskell』という本の主眼でした。

この『プログラミングHaskell』の改訂が出たというのが、この記事で唯一記憶して頂きたい話です。

第3波

3つめの波は、『すごいHaskellたのしく学ぼう』と『関数プログラミング入門 Haskellで学ぶ原理と技法』が出版された2012年です。

『すごいHaskellたのしく学ぼう』(2012年) 『関数プログラミング入門 Haskellで学ぶ原理と技法』(2012年)

『すごいHaskellたのしく学ぼう』は、おそらくこの記事を読んでいる人全員が少なくとも書名だけは知っている本だと思います。 へたうまなイラストに目がいきがちですが、「Haskellの解説手法」という観点でもひとつの金字塔を打ち立てた本で、その意味でも「すごい」本でした。

具体的には、関手(ファンクター)→アプリカティブ→モナドという解説の流れは、この本が作り出しました。 この説明の流れは、「同じパターンで定義できる関数」をHaskellでどのようにまとめるかについて、『Real World Haskell』のころには知られていなかった知見を反映したものです。

これ以降に出版された解説書では、この流れを意識するか踏襲するかして、モナドが解説されていると思います。 それ以前はどうだったかというと、アプリカティブを経由せずにモナドへと一気に直登するハードモードが一般的でした。 実際、同じ年に翻訳は出たものの原書が出版されたのは1998年である『関数プログラミング入門 Haskellで学ぶ原理と技法』は、このハードモードです。 そのころにはまだアプリカティブが発見されてなかった(よく知られてなかった?)からしかたありません。

では『関数プログラミング入門 Haskellで学ぶ原理と技法』には今では価値がないのかというと、そんなことはなくて、等式変形によって関数を作り出す「運算」の方法を学べます。 この運算、より応用的なHaskell本である『関数プログラミングの楽しみ』や『関数プログラミング 珠玉のアルゴリズムデザイン』では大活躍します。 ただ、次に紹介する第4波において本書の改訂版の翻訳にあたる『Haskellによる関数プログラミングの思考法』が出ているので、いまならそちらを読むほうが楽かもしれません。

第4波

第4の波は、2017年前後のバラエティー豊かなHaskell解説書の登場です。 あまりにバラエティーが豊かなうえに、時期的にも隔たりがあるので、全部ひっくるめるのはちょっと乱暴という自認はあるんですが、とりあえず並べます。

『関数プログラミング実践入門 増補版』(2016年、最初の版は2014年) 『Haskell 教養としての関数型プログラミング』(2017年) 『Haskell入門 関数型プログラミング言語の基礎と実践』(2017年) 『Haskellによる関数プログラミングの思考法』(2017年)

関数プログラミング実践入門』は、実際にはHaskellに限定した本ではありません。 ただ、他の言語におけるモナドの話とかも入ってるのでむしろお得だし、Haskellの実務的な側面に対する解説に価値がありました。 個人的には、GHCを使って開発するときに知りたかったことが書いてあって、とてもうれしい本でした。

追記:2014年に最初の版が出ている『関数プログラミング実践入門』がこの第4波に含まれているのは、ぼくが最初の版を2016年だと勘違いしていたからです。当然、記事も間違っていたのですが、同書の著者である大川さんの指摘で上記の表も修正してあります。

Haskellによる関数プログラミングの思考法』は、Haskellそのものですが、実務的なプログラミングではなく、運算の技法を通して関数プログラミングの何たるかを説明する本です。

Haskell 教養としての関数型プログラミング』と『Haskell入門 関数型プログラミング言語の基礎と実践』については、 ちゃんと読んでいないので内容については何も言えないのですが、 目次を見る限り関手→アプリカティブ→モナドにそってHaskellをひととおり理解できることが目指されているのだろうと感じています。 特に『Haskell入門』のほうは、実務でよく見かける例題が豊富にあるようで、今では古くなってしまった『Real World Haskell』の位置を埋めてくれる本のように思います。

『プログラミングHaskell』が改訂されます

ここからは宣伝です。 第2波で紹介した『プログラミングHaskell』は、原書は2016年に改訂されていたのですが、これがついに翻訳されて発売が開始されました!

www.lambdanote.com

上記では、旧版である『プログラミングHaskell』について、こんなふうに要約してました。

「(変数への代入ではなく)関数の組み合わせで高度なプログラムの全体を構築していく方法をHaskellというプログラミング言語で解説していく」ことが、『プログラミングHaskell』という本の主眼でした。

第2版でも、この主眼はまったく変化していません。 (関数)プログラミングそのものを学ぶための教材としてのHaskellの解説書というコンセプトはそのままに、 Haskellのコードの見た目が数式による表現から等幅フォントによる表現に変わったり、 解説に使われているHaskellの実装がHugsといういまでは滅多に見かけない処理系からGHCになったりしています。 もっとも、処理系に依存した話はもともとあまりないし、これらはあくまでも表面的な変化だといえます。

では、第2版は旧版から表面的な改修を施したものにすぎないのでしょうか?

もちろんそんなことはありません。 第2版で個人的にもっとも進化したなと感じてるのは、型クラスに基づいて諸概念が整理された結果、 「型を使ってプログラミングするとはどういうことか」をより実感できるようになった点だと思っています。

第2版ではプログラミングにおける型の理解が深まると思う

個人的に、Haskellの型って、大きく分けて以下の2種類の用途で使ってるような気がするんですよね (ぼくがHaskellで何かを書いてるときにこう考えてる、という話なので、ピントがずれてる可能性はあります)。

  • データとしての型
  • 定義が同じような形をしている関数を、同じ仲間だと思って扱うための道具としての型(型クラス)

前者は、いわゆる代数的データ型として、Haskellらしいプログラミングのスタイルの根っこにあるやつです。 解きたい問題を「型」でとらえて、その型の上でパターンマッチとかしまくって処理を宣言的に書いていくというのが、ぼくのなかではHaskellらしいプログラミングです。 型のこういう側面については、もちろん初版でも強調されて説明されてたんですが、この第2版では前半を上回る物量でそれを体感できます。 ここで物量と言っているのは、単なるページ数の増加ではなく、例題の増強です。 にもかかわらず、説明のスタイルも、この間の他書における解説の進化に影響されてか、全体にかなりすっきりとした姿になっています。

後者における型については、旧版ではまったくと言っていいほど説明には出てきてませんでした。 実際のところ、Haskellで書き捨てのプログラムを書いているようなときは、あまりこういう観点で型を使う機会はない気がします。 定義の形が同じものを同じように扱うというとLispのマクロが思い浮かびますが、 Lispのマクロも、最初から「よしマクロだ」と意気込んで導入することは少なくて、 なんか関数をいくつか書いてみたら同じような形が現れるのでマクロにできるな、みたいな感じで導入することが多いと思うんですよね。 もちろん、イディオム的に最初からマクロが使えそうだと判明しているケースはあるし、人によってはマクロでプログラムを考えられるのかもしれないので、あくまでも個人の経験に基づく感想です。

で、話を戻すと、Haskellではそういう「なんか同じようなパターンだからまとめたい」も型ベースで扱います(Lispのマクロと背景でなにか繋がってたりするんですかね?)。 とはいえ、すでに触れたように、ぼくみたいなゆるふわなHaskellユーザーが自分でそういうパターンの抽象化を考える必要に迫られる機会はあまりありません。 なにせ型ベースなので、Lispのマクロより数学的な制約があって、そのためモナド則みたいなのを考えないといけないし。

でも、いまのHaskellって、アプリカティブとかFoldableとかTraversableみたいな、型ベースで抽象化済みの仕掛けが標準で入ってるんです。 なので、わりと単純なプログラムでも、ほかの人がそういうやり方で抽象化してくれた土台に乗っかって自分のプログラムを書くことになります。 だから、少なくとも抽象化のノリを理解できていないと、Hoogleで関数を検索するのも難しくなってしまった。

そんなわけで、この第2版でも、モナドの説明がアプリカティブ経由になったり、Foldable/Traversableを説明に取り込んだり、そういうのをしっかりとやっています。 説明を表面的に増やしただけでないので、結果として型の後者の側面にも光が当たることになり、型についての理解が旧版よりも深まってしまう、そんな一冊になってるような気がしています。

ここで買えます

プログラミングHaskell 第2版(紙書籍+電子書籍)www.lambdanote.com

以下はラムダノートのお知らせページからの引用。

「紙書籍+電子書籍」版のお求めでも、「電子書籍のみ」版のお求めでも、いますぐPDFのダウンロードが可能です。紙書籍については、8月22日(日)以降の発送開始を予定しています。書店(オンライン書店を含む)での紙書籍の発売も8月22日以降を予定しております。

なお、直販サイトでのお求めにあたってユーザ登録などは不要です(ただ、ユーザ登録をしていただくと、購入済み電子書籍の再ダウンロードが簡単にできたり、ときどき思いがけない特典があったりします)。

『n月刊ラムダノート Vol.1, No.2』を読むべき1つめの理由

『n月刊ラムダノート』の話をいろいろしたいのだけど、どこから話せばいいのかわからないので、Lispの話をします。

昔、といってもほんの10年ちょっと前のことですが、日本でLispが流行った時期がありました。 「プログラミング言語のパワーには絶対的な差が存在する。その頂点に立つのがLispだ」と言って憚らない『ハッカーと画家』という本が2005年に出版され、それを読んだ多くの人が「よろしい、ならばLisp」と思ったのです。

まあ、ほかにもいろいろな理由があったのだろうし、流行に関係なくLispを使い続けている人はたくさんいたし、いまでもぼくを含め多くの人がLispを日常的に使っているけれど、『ハッカーと画家』の影響によるちょっとしたLispブーム、というのは確かに起きていたと思います。

で、この『ハッカーと画家』を翻訳したのが川合史朗さんでした。 その当時、ぼくは同書を企画した部署にたまたまいて、その制作の様子をちょっと横で見てたりもしたんですが、川合さんはハワイ在住ということもあって面識があるわけでもなく、同書の訳者としての川合さんは実はよく知りません。

ぼくにとって川合さんは、むしろGaucheの作者としての川合さんです。 Gaucheというのは、Lispの二大巨頭のひとつであるSchemeの実装です。LispとかSchemeとかを抜きにしても、日本語の処理が楽にできた高機能なスクリプト言語でした。 すべてがUTF-8になった現在、たいていの言語で日本語の扱いに難儀することはあまりないですが、そのころは日本語に対して何も考えずに正規表現を使えるというだけでGaucheは魅力的なプログラミング環境だったのです。 もちろんPerlRubyはあり、それらを仕事や趣味に使ってはいたんですが、Gaucheの存在を知って使ってみたところ、「S式はいろいろ快適」という事実を思い知ったのでした。

「S式はいろいろ快適」には、もうすこし補足が必要かもしれない。

ぼくの仕事は書籍の編集者なんですが、書籍の編集者というのはXMLをよく扱います。 XMLの構造を扱うのに、ふつうはXSLTというプログラミング言語を使うのですが、ぼくはこれがあまり好きじゃなかった。 XMLは、要するに木構造なので、そのままS式に見えるし、それならS式として扱いたいわけです。 実際、XMLのS式表現として、SXMLというものがあります。 ありがたいことに、GaucheにはSXMLのためのツール群もまるっと用意されていました。

というわけで、ぼくが日常的に使うプログラミング言語はもっぱらGaucheという状態になり、自然とGaucheを中心としたコミュニティにもどきどき顔を出すようになったりしました。Gauche Nightで発表したのは本当に素晴らしい思い出です。 Gaucheのコミュニティは、そのまま他のLisp系言語のコミュニティや関数型言語、特にHaskellのコミュニティなんかとも地続きで、結果として本当にたくさんのすごい人たちに出会うことができました。

いまも、大半のやっつけスクリプトにはGaucheを使います。 そこそこ大きめのプログラムを書くときはHaskellを使うし、ぶっちゃけXMLをいじるのもS式よりArrowのほうが脳が楽なことに気が付いたけれど、それでもプログラムを考えるときは脳内でまずS式をこねこねする癖は抜けないので、自分にとっての母国語は完全にLispであり、Schemeであり、Gaucheであり、その生みの親かつ育ての親こそが川合さんなわけです。

その川合さんが、Lispの記事を、自分がやってる出版社の逐次刊行物で書いてくれました。やったね!

n月刊ラムダノート Vol.1, No.2(2019)(紙書籍+PDF版)www.lambdanote.com

f:id:golden-lucky:20190726123033j:plain

しかも、まさかの「LISP 1.5」です。

Lispは、プログラミング言語としては歴史が古いので、現代的なコンピューターでプログラミングしているとあまり意識しなくて済むような概念や、いまでは別の考え方で把握され解決されているような困難にまつわる話がわりとちょくちょく出てきます。 そうした古の概念であるとか、過去の偉人たちが困難に対峙してきた物語もまた、Lispの魅力のひとつだったりします。

歴史の話、楽しいじゃないですか。 いまぼくらが入門書とか教科書という形で整理された内容を学べるのは素敵なことだけど、なぜ教科書にあるような形で内容が整理されており、それをぼくらが学ぶ必要があるのか、根っこを知ってはじめて見えてくる景色もあるわけです。

そう考えると、Lispのパワーっていうのは、そうやって理論と実践の縁をいったり来たりしながら、後代のプログラマーたちが発見することを別の形ですでに体得しちゃってるかもしれない側面なのかもしれない。 エモい話になりそうなのでこの辺でやめますが、そんなわけで、Lispをちょっとでもかんじると、今のプログラミングにおいて当たり前だったり、理論的には別の手段で解消されたりしている昔の話に、わりとよくぶつかります。

ここで、ぼくのようなLispと同時代を生きてきていない素人にとって辛いのが、そういうときに自力で原典に立ち返ろうと思っても話の文脈がいまいちよく見えないことです。 論文などに書いてある記述を素直に読み、文脈を補完する努力をすればいいのだけど、そこまでの余裕がなかったりするので、「話だけは聞いたことある」みたいなキーワードだけが脳内に積まれていきます。 「LISP 1.5」は、そんな「話だけは聞いたことある」というLispなキーワードのなかでも、かなり上位にあるやつでした。 (勝手な想像ですが、ぼくみたいな「ゆるふわLisper」はほかにもわりと多いような気がします。)

Lispをやっていないとわからないかもなんですが、「LISP 1.5」、Lisperをやっていると、本当に名前だけは耳にするんです。 そもそもLispを発明したのはジョン・マッカーシーで、彼は人工知能研究の一環としてリスト処理のための言語を考案したけど、最初は計算機で動かすプログラミング言語を考えているつもりはなかったらしい。 これをスティーブ・ラッセルが計算機上で実行できるように実装し、それを改良していろいろがんばったのが「LISP 1.5」だったらしい。 「らしい」ばかりなのは、ほんとよく耳にするけど少なくともぼくにとって「LISP 1.5」は文字通り「伝説」の存在だったからです。

ちょっと話はそれますが、Schemerをやっていても、やはり似たような感覚におそわれるキーワードがあります。 そのひとつは、「funarg問題」っていうやつです。 funargは、「function」と「argument」の造語なんですが、たぶん「ふなーぐ」と読みます。 これは要するに、関数の引数に関数を渡すと仮引数が衝突してしまうという話で、 Schemeはこの「funarg問題」を根本的に解消したLispであるという触れ込みがあることから、Schemeを勉強すると必ずといっていいほど目にすることになります。 しかし、プログラミング言語の教科書だと束縛変数の名前を暗に付け替えることで回避してるし、 関数プログラミングをしていても実際にハマることはほとんどないので、なんでSchemeがそこまで「funarg問題に煩わされない」ことを喧伝しているのかピンとこない。

さらに話はそれますが、プログラミング言語を独習したり人に教えたりしてると、「シンボル」が何なのかわからなくならないですか? 文字列みたいに使えるけど書き換え不能なやつ、みたいな理解でいいと思うんですが、これ、言語の処理系ではどうやって実現されてるんでしょうか。 いま、何か言語を作るなら、ルックアップテーブルを使ってシンボルを実現すると思います。 しかし、コンピューター上でシンボルを表現する方法はほかにないんでしょうか。

川合さんによる『LISP 1.5の風景』は、ぼくにとって、これらのモヤモヤをスッキリさせてくれる記事でした。

  • LISP 1.5が伝説たるゆえんは、「データ構造としてリストを使い、その操作と条件分岐と関数適用の仕組みを用意すれば、自分自身の評価が可能な評価器を作れる」をコンピュータで実践したことにあった!
  • funarg問題はLISP 1.5でも解消されていたけど、Schemeでは確かに根本的に解消されていた!
  • シンボルはルックアップテーブルを使わなくても実現できて、それもなんかうまい感じのデータ構造だけで作り出せてしまう!

ほかにも、こんな文字通りの風景が見えてくるようで、読んでいるだけで椅子から転げ落ちそうになります。

  • 初期のLispカッコを使わない表現、つまりS式ではなくM式という「いまのプログラミング言語に近い見ため」だったこと
  • ということは、「LispでつくるLisp」が容易なのはLispシンタックスがデータ構造と同じだからというわけではなく(おかげでパーズは楽だけど)、ラムダ抽象と関数適用を濃縮した言語であることによるのかもしれない
  • というか、むしろ「LispでつくるLisp」ネタから出発してラムダ抽象と関数適用を濃縮し、それをコンピューターで動かすときの問題をひとつずつ克服していった一つの形が「LISP 1.5」だったのではないか

しかも、川合さんのおかげで、今のコンピューターで動かすとちゃんと動くようになっている。手を動かしながら、こういう「なるほどー」を味わえるようになっています。

github.com

そんな川合さんの「LISP 1.5の風景」が掲載されている『n月刊ラムダノート Vol.1, No.2』は、人工知能の代名詞の座をLispから奪ったディープラーニングと、これからその座を奪うかもしれない量子コンピュータの記事が一緒に読めて1500円。ぜひ読んでみてください。

n月刊ラムダノート Vol.1, No.2(2019)(紙書籍+PDF版)www.lambdanote.com

f:id:golden-lucky:20190726123857j:plainf:id:golden-lucky:20190726123909j:plain

購入は(いまのところ)直販サイト限定ですが、ユーザー登録はとくに不要ですし、クレジットカード番号や住所をうちのサイトに直接入力しなくてもAmazon PayやGoogle Payでさくっと通りすがりに購入できます。

www.lambdanote.com

技術書の技術を軸に技術書について想いを馳せた話

ここでいう「技術書」というのは、「IT系の技術周辺を扱った本」のこと。この意味における「技術書」の界隈では、近年、「技術書典」という同人誌即売イベントが年二回のペースで開催されている。これは文字通りの祭「典」で、わずか6時間という短い開場時間に、数百人からなる同人誌の書き手とその作品を貴ぶ1万人の買い手が集結する。

IT系の技術ではソフトウェアが占める役割が大きく、ソフトウェアというのは技術的な変化が激しい。そのため、技術に関する情報を伝える手段にも、俊敏性とか即応性が求められる。紙の書籍というのは、一般にはそういう性能が低い、リジッドなパッケージである。それなのに紙の同人誌が技術情報の手段として尊ばれるのは、一見すると不思議な現象ではる。技術情報のアウトプットが目的であれば、自分ではてなブログに書いたり、Qiitaに書いたりでいいのでは?

にもかかわらず同人誌の書き手と読み手が尽きないということは、何かしら「本のようなパッケージに拘る理由」があるはず。そして、「本のようなパッケージ」という実体があるなら、それを実体ならしめている技術があるはず、ということで、同人誌を頒布してる人、商業出版してる人、どっちともつかないけど明らかに界隈の人、などが集まって言いたいことを言うパネルセッションの機会をCROSSを主催している方々からいただいた。

cross-party.connpass.com

というわけで、以降はこのイベント後半のパネルディスカッション「技術書を作るテクニック」の個人的な振り返り。

個人的に親交がある人から見ると「おまえ本当に司会やるの大丈夫か?」と心配されるような座組。

twitter.com

写真でみるとモデレーターっぽい(万井さんよい写真ありがとうございます)。

twitter.com

さて、お題は「技術書の技術」なんだけど、そんなフワフワなテーマで実のある話ができるわけもない。そこで、技術書典6の直後ということもあり、同人誌と商業誌の境界をパネラーに攻めてもらうという方針でやることにした。議論のためのフレームとして「本というパッケージを作って読者の手に届く状態にする」までの各段階における技術論を骨組みにすれば、「技術書の技術」というお題からもそんなに外れないよね、という理屈。

f:id:golden-lucky:20190426131307p:plain

パネラーのみなさんにこの方針をなんとなく伝えたのは前日で、それもgistに書いた趣旨を送りつけて「当日よろしく」ってしただけだったので、当日はそれぞれの視点から興味深い意見やエピソードを的確に投下してもらえてとても助かりました。ありがとうございました。

とはいえ、制作ツールの話を期待されていた方にとっては、この方針はもしかすると期待はずれだったかもしれない。でも、ぶっちゃけ制作ツールについては、商業だから特別製ということも、同人だから新しいツールを実験できるということもなくて、どっちもすでに似たような環境が利用可能である。もちろん、パネラーの花谷さんから意見が出たように、ツールまわりの知見の入手しやすさとかは同人では壁があるのかもしれないし、実際、そればっかりやってる人間が関与してる商業出版のほうが強い。プロゆえの使いこなしとかもあるだろう。それでも、同じツールを使って「本っぽいもの」にすること自体はかなりたやすくなってるので、その点で同人と商業に本質的な差はないと思うんだよなあ(本質とは?)。

むしろ、制作ツールの外の技術にも議論のフレームを広げておいたことで、「ネタ出しの部分は(技術書だと)もはや出版社の中の編集者には無理」という見解を会場にいたインプレスR&Dの山城さんから引き出せたりしたから、まあまあよかったのかなと思っている(ちなみに個人的にはその見解とは逆の見解を持っている)。あと、ニュートラルに本と出会える場としての「書店」の意味(パネラー鈴木さん)とか、気軽なアウトプットの場のオルタナとしてQrunchの話がでてきたり(パネラー和田さん)とか。

あ、細かい話題については拾っている余裕がないので、togetter(長い)をご覧ください。

togetter.com

なんだかなんだいって全体の大きな話の流れとして、技術情報のアウトプットしやすさもインプットしやすさも「本っぽいやつ」はバランスが取れてて強いよね、というトーンがあったと思う。しかし、「本っぽさ」を超える技術を手に入れて進みたいよね、それが電子書籍っていう感じでもないのが残念だよね、という話もちゃんとできた。このあたりの話題はパネラーに高橋さんがいたおかげもあって浮ついた話にならず、ほんとうによかった。

結局、技術書については、同人と商業に(技術的な視点から)境界を見出す必要はない。それはいまの実体をみてもそう。その中間のグレーゾーンにおける取り組みが広がりつつある。

f:id:golden-lucky:20190426131321p:plain

そして技術的な視点を別にしても同人と商業の境界を見出すことにはそれほど意味はないし、歴史的にも商業出版みたいな概念が同人から分離していた期間はそれほどないよね、書きたい人が書いて、読みたい人が読む、それが経済になるなら、そのために必要なお金を回す仕組みを作る、そのひとつとして技術書典なりグレーゾーンでの活動なりがいろいろ登場するのではないか、みたいな話でいい感じに収まったような気がする。日高さんもパネラーとしてTechBoosterや技術書典はその中間を滑らかにつなぐんだっていってたし。個人的には、こういう揺り戻しをぼくらはいまリアルタイムで見ているところなのかな、と改めて思った。

twitter.com

パネラーのみなさま、会場のみなさま、特に議論に参加していただいたみなさま、CROSSのみなさま、刺激的な機会をいただき、本当にありがとうございました。

QUICとHTTP/3時代のインターネット解説書はどうあるべきだろう

OSI参照モデルTCP/IPモデル

かつてぼくたちは、7つのレイヤに分かれたOSI参照モデルという姿でコンピュータネットワークを学び、その7層のモデルにそって各種のプロトコルを理解しようとしていました。 だから、「SONET/SDH上のATM回線でIPパケットをやり取りする」という構想をきけば、「つまり、SONET/SDHがレイヤ1で、ATMがレイヤ2で、IPがレイヤ3なのだな」という枠組みを頭に描いていました。

と同時に、OSIのレイヤとはいったい……、というアンビバレントな想いにさいなまれることもよくありました。 「SONET/SDHがレイヤ1っていうけど、これレイヤ2の話も含まれてるような気がする」とか、逆に、「ATMってレイヤ2とされるけど、レイヤ1の話も含まれているような」とか。

それでもまだ、このへんの低レイヤのネットワーク技術は、OSI参照モデルの各層になんとなく合致しているような雰囲気がありました。 本当に混乱するのは、レイヤ5以上です。 ぼくの稼業はネットワーク技術の解説書を作ったりすることなんですが、レイヤ4を下位層として利用するさまざまな技術に対し、誰もが納得するようなOSI参照モデルの階層を与えるという仕事は、いつだって絶望的でした。 それでもなお、現在のようにすべてがインターネットになる以前は、OSI参照モデルの枠組みで理解すると都合がよいプロトコルスタックもありました。 OSI参照モデルに立脚した技術解説を与えることは、インターネットの時代になっても十分に意義があることだと自分を思い込ませながら、解説書を作っていました。

そうこうするうちに、コンピュータネットワークの世界は、事実上インターネットと互換な技術ばかりになります。 それらインターネットを前提に開発された技術は、OSI参照モデルに準拠して開発されたわけではありません。 OSI参照モデルのレイヤという枠組みで、それらの技術をすっきり説明することは、ますます絶望的になりました。

実際のところ、そうしたインターネット前提の技術(長いので以降ではインターネット技術と総称します)をうまく説明するだけなら、OSI参照モデルのすべてのレイヤについて語る必要はありません。 インターネット技術の中核となるのは、パケットをずっと先まで転送するための「IP」と、転送されるパケットをさらに上位のアプリケーションに結び付ける「トランスポート」です。 この2つのレイヤと、その上下のレイヤの合計4つだけを見れば、インターネット技術について語るには十分でしょう。 このようなモデルを、ここでは「TCP/IPモデル」と呼ぶことにします。

(IP、トランスポート、その上下のレイヤという4つの層にゆるく分けてインターネット技術を説明するモデルは、インターネットに接続する端末の技術要件を整理したRFC 1122にまとめられています。 RFC 1122では、この説明モデルの名前を特に定めていませんが、一ヶ所だけ「インターネットモデル」という言い方をしているので、ひょっとすると「TCP/IPモデル」よりも「インターネットモデル」と呼ぶほうが正確かもしれません。 しかし、この記事では、このモデルをモデルたらしめている部分を担うIPとTCPというプロトコルを強調したいので、「TCP/IPモデル」と呼ぶことにします。)

なぜいまでもOSI参照モデルによる説明が多いか

ここまでのおさらい。

  1. かつてOSI参照モデルというものが論じられ、ネットワーク技術の解説でもそれに沿ったアプローチが採用された
  2. ネットワーク技術の多くがインターネット技術と同義になっていくなかで、その技術の理解を促すのにOSI参照モデルは最適とはいえず、TCP/IPモデルと呼ぶべきモデルが考えられた

しかし、インターネット技術の解説においてTCP/IPモデルはあまり採用されていないように見えます。 むしろ、旧来のOSI参照モデルに照らしてインターネットを支える各種のプロトコルを説明していくスタイルの解説書のほうが優勢でしょう。

なぜ、インターネット技術の解説でも、OSI参照モデルが依然として採用され続けているのでしょうか?  解説のアプローチをTCP/IPモデルに移行できない根本的な理由があるのでしょうか?

理由の1つとして考えられるのは、「レイヤ2ネットワーク」とか「レイヤ3スイッチ」のようなOSI参照モデル由来の用語がそれなりに普及しているという現状でしょう。 これらの用語について説明するには、「2」とか「3」といった数字が何を意味するのかを解き明かす必要があり、そのためにはOSI参照モデルについてまったく紹介しないわけにはいきません。

そしてもう1つ、私見ではこちらのほうが理由として大きいと想像しているのですが、説明方法を変更するリスクを負いたくないという解説側の思惑があるような気がしています。 現代のネットワーク技術について説明するには、レイヤという概念がとても便利です。 なにせ、前述したRFC 1122でも、「きっちりレイヤを分けたモデルでインターネット技術を考えることは、それが仕様であれ実装であれ不完全(な理解しかもたらさない)」と明言しつつ、要件の整理には4レイヤからなるTCP/IPモデルを採用しています。

データをカプセル化し、あるプロトコルが提供する機能を別のプロトコルから呼び出して使うという抽象的なインターフェースを理解するには、プロトコルが層になったモデルで考えるのが手っ取り早いでしょう。 その概念を教えるためのアプローチとして、長年にわたり採用されてきたのが、OSI参照モデルです。 レイヤという、だいぶ説明がめんどくさい概念について、いままでの解説と同じ枠組みをそのまま再利用したいという要求があったことは想像に難くありません。

都合がいいことに、インターネット技術の中核となっているIPとトランスポートは、それぞれOSI参照モデルにおけるレイヤ3とレイヤ4にだいたい対応していました。 そのため、OSI参照モデルについて説明した部分は従来のまま、「このうちのレイヤ3がIP、レイヤ4がトランスポートです」という感じに説明を済ませることができます。 インターネット技術について解説するにあたり、OSI参照モデルを破棄する必要は特になかったのです。

こうして、技術理解にとって最適な枠組みはOSI参照モデルからTCP/IPモデルに変わったにもかかわらず、その解説ではOSI参照モデルが採用され続けるという状況が続きました。 OSI参照モデルの7つのレイヤをすべて意識し、そのすべてに個々のインターネット技術を対応させないといけない状況は、こんな具合にして形成されるに至ったのだと思われます。

QUICは、TCP/IPモデルのトランスポートとはいえるが、OSI参照モデルのレイヤ4とはいいにくい

ここからが本題です。

いま、TCP/IPモデルにおけるトランスポートを担う新しいプロトコルとして、QUICが標準化されつつあります。 このQUICの役割を解説するにあたって、ぼくらはいよいよ、OSI参照モデルというネットワーク技術の解説方法を手放すべきではないでしょうか。

これは、これからはOSI参照モデルではなくTCP/IPモデルを使ってレイヤの考え方を解説すべきという話ではありません。 むしろ、金科玉条のモデルはないという事実を受け入れて、もっと別の説明の仕方を考えないと、どう説明しても無理が生じる(場合によっては間違いと指摘されかねない)状況に陥るだろう、その好例がQUICではないか、という私見です。

QUICは、OSI参照モデルに押し込めればレイヤ4にあたりますが、たとえばTCPの代わりにQUICをそのまま使えるわけではなく、同じレイヤ4のプロトコルであるUDPが必要です。 OSI参照モデルにのっとった解説だと、この時点でもうすっきりしません。 UDPでレイヤ4の機能を実現しているのだから、QUICはレイヤ5、あるいはレイヤ6になるのでは?  実際、そういう側面もあります。 OSI参照モデルにのっとった解説では、TLSの機能をレイヤ5とかレイヤ6に分類することがありますが、QUICにはTLSで実現していた機能も含まれています。 そう考えると、QUICのことをレイヤ4のプロトコルとして考えるのは間違っているようにも感じられます。

では、いっそのことQUICをレイヤ5からレイヤ6を統合したプロトコルと説明するのはどうでしょうか?  それはそれで、QUICをTCP/IPモデルにおけるトランスポートプロトコルとみなす説明と矛盾しそうです。 トランスポートプロトコルだといえるけど、OSI参照モデルの枠組みで説明しようとするとレイヤ5や6に区分されてしまう、そういう役割にQUICという名前が与えられた、というのが実態に近い気がします。

そもそも、なんでこんな、従来のモデルにおけるレイヤ分けに合致しないプロトコルが誕生したのでしょうか?  この疑問について考えることが、おそらく、これからのネットワーク技術の解説の仕方を考えることになると思います。

ぼくの現在の考えを先に言ってしまうと、OSI参照モデルであれインターネットモデルであれ、既存のレイヤのモデルを頭に思い浮かべてそれに沿ってQUICを説明しようとすると、たぶん破綻します。 プラトンイデア的な理想のレイヤのモデルはありません。 ぼく自身、そういう理想のレイヤの存在を暗に前提していて、そのうちの1つのレイヤを担うプロトコルとして開発された技術という視点でQUICをとらえようとした結果、上記のようにわけがわからなくなっていました。

それなら、そういう視点を捨ててネットワーク技術のトレンドからQUICの意義を素直に考えて、そのうえで来るべきネットワーク技術の教科書の姿を思い描いてみようというのが、この記事で言いたいことです。

HTTP/QUICモデル

QUICのことを、TCP/IPモデルに当てはめると、トランスポートを担うプロトコルとされます。 しかし同時に、QUICは、TCP/IPモデルにおけるトランスポートとしてUDPを使います。 この2つの事実は、きっちりレイヤ分割されたモデルの世界観に照らして考えると、どうにも矛盾しているような気がします。 しかしこれは錯覚で、実際には上下2つの観点から冷静に技術トレンドとして考えるとすっきり説明がつきます。

まず上から考えましょう。 現在のインターネットを支配しているサービスはウェブです。そして、そのウェブを担うプロトコルHTTPです。 HTTPは、従来はトランスポートとしてTCPを利用しています。

HTTPの未来について考えている人たちは、ここで、トランスポートとしてTCPを使わずにHTTPをやる方法はないかと考えました。 なんでそんなことを考え始めたかというと、TCPのやり方をハックして接続を確立するまでの手間を減らしたり輻輳制御や再送処理をいじったり並列に接続を張って多重化したりしたいけど、TCPの機能はOSに組み込まれていて簡単には変更できないから。 で、そういう仕掛けをHTTPに組み込んだ魔改造HTTPの開発に着手しました。 この魔改造HTTPには、必然的に従来のTCPで提供されていた機能も含まれます。 そこで、この魔改造HTTPを開発したGoogleは、これを「トランスポートの機能を提供する新しいプロトコル」として発表しました。 これがQUICです。 Googleは、以前にも魔改造HTTPを作ってインターネットで運用し、HTTP/2として標準化までもっていきましたが、こちらは最初からTCPを利用するプロトコルとして開発されました。)

ここで問題になるのが、このGoogle魔改造HTTPであるQUICをどうやってインターネットに載せるかです。 トランスポートなので、可能ならIPv6の上に直接載せたいくらいだけど、そんな通信を許してくれるように世界中の通信機器を変更することはできません。 そこで、既存の中間機器すべてで既に対応済みであろうUDPを使うことにしました。 これにより、途中の機器ではUDPポート80や443を通ってやりとりされるパケットとして、この魔改造HTTPをいまのインターネットにそのままのっけることが可能になりました。 これが、QUICを下からみた場合の実体です。 要するに、UDPを使うことはQUICにとって本質的な要請ではなく、たまたま運用上都合がよかったからです。 なんでこれをわざわざ強調するかというと、QUICが登場したころの記事では「UDPなので速い」という論調の解説が多くあり、ぼくもつい最近まではそれを真に受けていたからです。

さらに、このGoogleが実装した魔改造HTTPとしてのQUICは、IETFで標準化されるにあたり、本来のHTTPっぽい部分は分けて考えられるようになりました。 QUICのうち、このHTTPよりの部分だけを指すときには、「HTTP over QUIC」という表現が使われるようになります。 この部分には、その後、「HTTP/3」という正式名称がつきます。

こんなふうに、現在標準化が進められているQUICを見ると、最初から汎用のトランスポート技術として開発されてきたわけではなく、HTTP中心の世界観からの要請で出発していることがわかります。 実際、QUICの最初の標準は、HTTP/3に特化したものに限定することが合意されているようです。 QUICをTCP/IPモデルでトランスポートとしてすっきり説明できないのは、こう考えると当然であるように思えます。 むしろQUIC(とHTTP/3)について解説するときは、HTTP/QUICモデルという、TCP/IPモデルとはまた別の見方をしたほうがいいのではないかと思えるくらいです。

QUICをどう解説するか

さて、本記事的に肝心なのはここからです。 TCP/IPを中心にした世界観から、HTTP側の要請で従来のTCPのさまざまな機能を提供しようという世界観になりつつある中で、インターネットの仕組みをどう解説するか。 ぼくはQUICもHTTP/3も具体的な仕様に熟知しているわけではないので、解説そのものは書けませんが、どういう構成にするのがいいかなという妄想ならできます。

おそらく、まず必要になるのは、TLSの仕組みと世界観をしっかり説明することでしょう。 なぜなら、QUICによるHTTP通信は、すべてTLSの仕組みが組み込まれた暗号通信だから。 ここでいうTLSの仕組みの理解には、接続の確立手順や暗号の合意のためのネゴシエーション手順の説明だけでなく、証明書を利用したインターネット信頼モデルの理解も必要です。

とはいえ、この部分については、「なにはともあれ『プロフェッショナルSSL/TLS』を読みましょう」でいい気もします。 『プロフェッショナルSSL/TLS』を読むと、認証付き暗号(AEAD)の考え方がわかるので、さらによい。 QUICでは、ほとんどのパケットはAEADで保護されるし、トークンの相互認証を使ったステートレスな接続終了とかもあるので、AEADを理解させないとたぶん概略すら説明できない気がします。

プロフェッショナルSSL/TLS(紙書籍+電子書籍)www.lambdanote.com

で、そのうえで、まずはHTTPの説明なんだろうなあ。 HTTPの部分については、とりあえず渋川さんの『Real World HTTP』があるし、これが改訂されれば当面は十分そう。 あるいは、ウェブ屋さん向けにコンピュータシステムの低レイヤの話を説明するという『Goならわかるシステムプログラミング』という本があります。 ふつうのコンピュータシステムの本だとTCP/IPやソケットの話が入るところで、この本ではHTTPの話を厚めにしています。 上に書いたようなQUICの背景をふまえると、ウェブ開発者にとってのHTTP/3は、ネイティブアプリのプログラマーにとってのTCPのような位置づけになるわけで、システムプログラミングと冠した『Goならわかるシステムプログラミング』でHTTPを扱うのはまったくもって自然だったのだなあ(たぶんあとづけ)。

Goならわかるシステムプログラミング(紙書籍)www.lambdanote.com

さらに、ネットワーク技術の解説書としては、再送制御とか輻輳制御の話もQUICとは独立に解説してあげないと意味がなさそう。 QUICそのものとは別なので、汎用の専門的な解説書がほしいところです(計画進行中)。

もちろん、QUICの下はIPv6です(繰り返しになりますがUDPは本質じゃないよ)。 IPv6については『プロフェッショナルIPv6』があるので安心ですね!

プロフェッショナルIPv6(紙書籍+電子書籍)www.lambdanote.com

そんな感じで、これからQUICとHTTP/3が本格的に普及してくると、インターネット技術の教科書にも大胆な構成変更が求められるようになるかもしれません。 具体的には、トップダウンでこんな構成にするのはどうですかね。

第1章 ウェブの概要
第2章 HTTP
第3章 インターネットの信頼モデルと認証付き暗号
第4章 QUICとその一部としてのTLS
第5章 トランスポートの信頼性に関するさまざまな技術
第6章 IPv6の概要

興味がある専門家の方からのご連絡をお待ちしています!(一部の章だけでもいいよ!)

TeXのフォントまわりについては、以下の3点が意識して語られるといいなと思う。

  1. TeXのフォント環境の理解は難しい。なぜなら、フォントに関する深い知見はもちろん、今となっては古臭いTeXディレクトリに関する知識も必要だから
  2. 今となってはどうしようもない古臭い設計を、TeX Liveが現代的なインターフェースでラップしてくれているので、ぼくらは古臭いTeXの知識がなくても日本語PDFが作れる
  3. 古臭い設計を捨て、現代的なOSのフォント環境で動かすことを前提に開発されている新しいTeXもある(LuaTeXやXeTeX)

1については、特効薬がない。TeX Wikiに書いてある部分もあるけど、たとえばTEXMFLOCALが何かとかを知るには、まずKpathseaについて知る必要がある。つまり、texdoc Kpathsea とかする。Webブラウザで見るなら Kpathsea: A library for path searching にある。これを読むと、TeXディレクトリ構成、すなわち、TeXの実行に必要なファイルの配置場所、そこにPATH的なものを通す方法などがわかる。PATH的なものが通っている場所から必要なファイルの存在を確認するためのコマンド kpsewhich についてもわかる。TeXはあくまでもこのKpathsea経由でファイルを探すので、Kpathseaについて知らないとTeX関連の環境整備は何もできない。しかし、Kpathseaのマニュアルを読んでも、最初は何が書いてあるのかさっぱりわからないんだ。とくにフォントは、さまざまなツールで扱いが異なるし、フォントの種類によっても扱いが異なる。そもそも、フォントの実体ファイルをKpathseaから見えるようにしただけでは、TeX組版で使うことはできない。だから、Kpathseaのマニュアルは、折にふれ何度も眺めることになる。というわけで、1については、特効薬がない。

それでも日本語のPDFをぼくらがTeXで作れてしまうのは、TeX Liveのおかげである。もちろん、だからといってTeX Liveが完璧なツールというわけでもないけど、TeXでフォントがどう扱われるかを完璧に知らなくても済むというだけで、だいぶありがたい。かつてAppleのOS上のTeXで利用することが流行し、現在ではAppleによって古いTeXから見にいくのが困難な状態にされてしまったヒラギノを引き続きmacOSで使えるようにするパッチも、TeX Liveの本体とは別に毎度作られている。いろいろな事情や思惑があって大々的には配布されていないようだけど、OSSなので、プロプラなシステムに対応するためのパッチをどういう形で公開するかに文句はいえないと思う。

で、3です。いつまでも古臭い設計を引きずるのはいやだという勢力があって、そういう方向での進化も続いている。具体的にはLuaTeXやXeTeXというプロジェクトで、これらはTeXの旧来のフォント利用方法ではなくシステムフォントを使うことを前提にして開発されている。これらで日本語を問題なく使えるようにするプロジェクトも進んでいて、ある意味ではすでに十分実用的になっている。もちろん後方互換性は気にしないで開発されているので、匿名の誰かがTeX Wikiにまとめた情報だけで今までと同じように使い始められるようなものでもないけれど、「macOSでどうしてもシステムフォントをTeXで使いたい」という目的には、むしろそっちの道を模索して開発に貢献する道のほうが適切なのではないかなと最近では考えている。

%.texという名前のファイルをLaTeXで処理する方法

ふつう、コマンドの引数にファイル名を指定すれば、そのコマンドの実行プロセスに標準入力経由でファイルの中身が渡されると思いますよね。 ところがplatexとかpdftexなどのコマンドは、引数として指定されたものを、すべてTeXトークンであるかのように読み始めます。

ただし、そうやって読み始めるとき、「先頭の文字が \ でない場合だけは、その前に \input がついているとみなす」というルールがあります。 このルールのおかげで、たとえば次のようにコマンドを実行すると…

$ pdftex foo.tex

次のように実行したのと同じことになります。

$ pdftex \\input foo.tex

これはつまり、次のような1行だけを中身として持つファイルをpdftexコマンドに指定して実行したのと同じことです。

\input{foo}

したがって、もし次のようにすれば…

$ pdftex %.tex

次のような1行だけを中身として持つファイルを実行したのと同じことになります。

\input{%}

これらは、事実上同じというレベルでなく、完全に同じです。 なのでpdftexコマンドは、エラーを吐くでもなく、単に \input{ の続きをユーザが入力してくれるのを待ち続けます。

$ pdflatex %.tex
This is pdfTeX, Version 3.14159265-2.6-1.40.19 (TeX Live 2018) (preloaded format=pdflatex)
 restricted \write18 enabled.
entering extended mode

*

この動作の裏をかけば、次のような手順で、%.tex という名前のソースをTeX(以降はpdflatexを例にします)で処理することはできなくはなさそう?

1. `%.tex` の1行めに `\catcode`\%=14` と追記する

\catcode`\%=14
\documentclass{standalone}
\begin{document}
foo
\end{document}

2. pdflatex実行時にファイル名の前に ``\\relax\\catcode\`\\%=12\\input`` と指定する

$ pdflatex \\relax\\catcode\`\\%=12\\input %.tex
This is pdfTeX, Version 3.14159265-2.6-1.40.19 (TeX Live 2018) (preloaded format=pdflatex)
 restricted \write18 enabled.
entering extended mode
LaTeX2e <2018-04-01> patch level 5
(./%.tex
(/usr/local/texlive/2018/texmf-dist/tex/latex/standalone/standalone.cls
Document Class: standalone 2018/03/26 v1.3a Class to compile TeX sub-files stan
 dalone
(中略)
Output written on %.pdf (1 page, 8374 bytes).
Transcript written on %.log.

どうしても % を含むファイルをTeXで実行したい場合は、「1をsedにやらせてから2をやる」みたいなシェルスクリプトを書くことになるのかなあ。

2018年でもEPSファイルをTeXで使う

はじめに

みなさん、EPSファイル、使ってますか?

近年、(La)TeXの文書作成においては、「EPSファイルを使うな」というマナーが確立しています。 マナーにはデフォルトで抗っていくということで、この記事では、現代的なLaTeX環境におけるEPSファイルの可能性を探りたいと思います。

もちろん、「EPSファイルを使うな」は、実際にはマナーではありません。 技術的な根拠があるベストプラクティスのひとつです。 したがって、この記事の真の目的は、以下の2点だといえます。

  1. ベストプラクティスをマナーへと貶めないために技術的な背景を知る
  2. 条件があえばEPSファイルにもまだまだ使い道があることを示す

コンピュータサイエンスには、"All problems in computer science can be solved by another level of indirection"(「コンピュータ科学のあらゆる問題は、別の階層を挟むことで解決できる」)という格言があります (David WheelerまたはButler Lampson)。 上記の2では、MetaPostというアプリケーションをindirectionとして、EPSファイルをTeXが直接扱える形式として利用する道を示したいと思います。

この記事について

この記事は、TeX Conf 2018にてLT発表した「2018年でもTeXでEPSを使う」で話す予定だった内容のメモを文章として清書し、「TeX & LaTeX Advent Calendar 2018」の4日目の記事としてまとめたものです。

こうして文章にしてみると、この記事を原稿として読むだけでも5分で終わらなそうな話だった。。

目次

(La)TeX文書においてEPSはどう扱われるか

LaTeXで画像を挿入したいとき、もっとも歴史があるのは、EPSファイルを\includegraphicsする方法でしょう。 EPS形式で用意した画像を取り込むという手法は、LaTeX以前からTeXのエコシステムでは一般的だったようで、plain TeXにもepsf.styという仕組みがあります。 epsf.styのマニュアルでは開発履歴も触れられており、それを見ると、かなり初期のころにクヌース自身が手を入れていたようです(参考)。

ここで注意が必要なんですが、TeXそのものには、一般のEPSファイルを解釈する仕組みがありません。 \includegraphicsを提供するgraphicxパッケージやepsf.styの仕事は、「最終的な印刷を担うデバイス」へとEPSファイルを丸投げするだけです。

では、「最終的な印刷を担うデバイス」は、どうやって丸投げされるEPSファイルを画像にする、つまり、紙に印刷したり、PDFに変換したり、あるいはベクター画像としてレンダリングしたりするのでしょうか? そもそも、「最終的な印刷を担うデバイス」とは何なのでしょうか?

昔ながらのTeXであれば、原稿はDVIへとコンパイルされます。したがって、最終的な印刷を担うデバイスは、「DVIウェア」などと総称されるソフトウェアです。

DVIウェアがdvipsであれば、原稿で指定されていたEPSファイルの中身は、出力されるPostScriptにそのまま埋め込まれます。 それをGhostscriptやAdobe Distiller、あるいはPostScriptプリンタに渡すことで、それらのPostScript処理系が最終的な画像を生成します。

DVIウェアがdvipdfmxであれば、EPSファイルを最終的な画像にするのは、一般にはGhostscriptというPostScript処理系の仕事です。 これは、dvipdfmxの設定ファイル(dvipdfmx.cfg)に、「一般的なEPSファイルがあったらGhostscriptを起動してPDFに変換する」という内容の処理がデフォルトで記述されているからです。

DVIウェアには、XdviやWindvi、あるいはdviout、あるいはEvinceのような、DVIをそのまま画面上でプレビューするものも多々あります。 これらのほとんども、PostScript処理系を自前でもっているわけではなく、外部のGhostscriptを呼び出してEPSファイルを何らかの形式の画像に変換して描画しています。

いずれの場合も、「一般的なEPSファイルを最終的な画像にするにはPostScript処理系が必要」という点がポイントです。 これは、EPSファイルが、そもそもは一枚絵のために規定されたPostScriptのコーディング規約(DSC、Document Structring Conventionといいます)に基づくものであることを考えれば、当然だといえます。 PostScript処理系がないと、TeXのエコシステムは一般のEPSファイルを画像として再現できないというわけです。

そして、ここでいうPostScript処理系は、事実上Ghostscript一択です。 プロプラエタリなソフトウェアを利用せずに、PostScriptをベースとした技術を利用しようと思ったら、現状ではGhostscriptに頼らざるをえず、TeXもその例外ではないのです。

f:id:golden-lucky:20181204121414p:plain:w400
TeXエンジンはEPSを処理できない

「EPSファイルを使うな」の意味

前節で見たように、PDFが出力となるようなTeXのエコシステムでは、一般にはTeXのソースでEPSファイルが出てくるたびに外部のPostScript処理系(事実上Ghostscriptであることが多い)が走ります。 常識的に考えてこれは効率が悪い。

そもそも、現在もっとも広く利用されているTeX組版エンジンであるpdfTeXは、ネイティブでPDFの取り込みに対応しています(参考)。 日本語環境ではdvipdfmxがよく利用されていますが、これもネイティブでPDFの取り込みに対応しています(参考)。 そのため、EPSファイルとして用意した画像をそのつどGhostscriptを呼び出して処理するのは完全な無駄であり、最初からGhostscriptでPDFに変換しておけば十分なのです。

これが、いま「EPSファイルを(La)TeX文書に取り込むときの画像に使うな」という主張の背景にある事情です。

pdfTeXやdvipdfmxがネイティブに扱えるEPS

EPSファイルが現代的なTeXのエコシステムにおいて扱いにくい代物であるのは紛れもない現実です。 PDFならTeXエンジンがネイティブで扱えるのに、そこでEPSファイルを使い続ければ、Ghostscriptという厄介なアプリケーションが作業フローに関与してしまいます。

とはいえ、長らくTeXにおけるデフォルトの画像形式であったEPSファイルを利用しなければならないドキュメントもあるでしょう。 アプリケーションがEPSしか出力してくれない場合もあります。 あらかじめGhostscriptでPDFにしておけばいいとも言えますが、管理しているファイルがPDFでは扱いにくいという事情もありえます。 なにしろEPSの中身はPostScriptであり、PostScriptを知っていればテキストエディタで直接編集できます。 テキストファイルなので、バージョン管理にも都合がいい。

はたして、Ghostscriptを起動せず、TeXのエコシステムでPostScriptを直接処理することは不可能なのでしょうか?

実は、TeXのエコシステムで直接処理できるようなEPSファイルがあります。 そのようなEPSファイルであれば、画像としてTeXの文書中で取り込んでも、処理中にGhostscriptが呼び出されることはありません。 EPSファイル中のPostScriptを、pdfTeXやdvipdfmxが、直接解釈して画像にしてくれるのです。

そのようなEPSファイルは、Purified EPSと呼ばれています。純化されたEPSというわけです。 具体的には、MetaPostというアプリケーションによって生成されたEPSだけがPurified EPSになります(参考)。

重要かつややこしい箇所なので、MetaPostとPurified EPSについてもう少し詳しく見ておきましょう。

MetaPostは、ベクター画像を生成するためのプログラミング言語です。 MetaPostについては、2018年のTeX & LaTeXアドベントカレンダーの北川さんの記事(参照)がとても参考になります。 たとえば、ここにラムダノートという出版社のロゴマークがありますが…

f:id:golden-lucky:20181204151846p:plain
ラムダノートのロゴ

このロゴは、こんな感じのMetaPostソースで再現できます。

prologues := 1;

beginfig(1);
drawoptions (withcolor (0.6,0.6,0.6));
linecap := butt;
linejoin := mitered;
fill (26.867201,14.046900)..controls (26.914101,11.453100) and (27.386700,8.511720)..(26.027300,6.519530)
    ..controls (23.898399,3.402340) and (19.093800,1.851560)..(15.031300,1.777340)
    ..controls (8.378910,1.660160) and (2.886720,6.957030)..(2.765630,13.613300)
    ..controls (2.644530,20.269501) and (7.945310,25.761700)..(14.597700,25.878901)
    ..controls (21.253901,26.000000) and (26.746099,20.703100)..(26.867201,14.046900)
    --cycle;
drawoptions (withcolor (0,0,0));
fill (111.976997,17.812500)..controls (110.969002,18.261700) and (109.785004,17.804701)..(109.339996,16.796900)
    --(109.188004,16.445299)..controls (109.188004,16.445299) and (109.140999,16.339800)..(109.046997,16.132799)
    ..controls (108.941002,15.929700) and (108.875000,15.621100)..(108.612999,15.234400)

(つづく)

なお、MetaPostによく似た名前のアプリケーションとして、クヌースが作ったフォント開発のための言語および処理系のMetaFontがあります。 名前がよく似ていることからわかるとおり、MetaPostはMetaFontに影響を受けていますが、もっとずっと新しい時代に別の人が作った別のアプリケーションです。 実際、MetaFontはPostScriptに埋め込めるビットマップフォントを生成するのに対し、MetaPostはPostScriptをEPSファイルとして生成します(MetaPostのPostはPostScriptのPostです)。

MetaPostの処理系は mpost というコマンドです。 上記のMetaPostのソースファイルlogo.mpmpostコマンドに指定して次のように実行すると、EPSファイルが生成されます。

$ mpost logo.mp
This is MetaPost, version 2.00 (TeX Live 2018) (kpathsea version 6.3.0)
(/usr/local/texlive/2018/texmf-dist/metapost/base/mpost.mp
(/usr/local/texlive/2018/texmf-dist/metapost/base/plain.mp
Preloading the plain mem file, version 1.005) ) (./logo.mp [1{psfonts.map}] )
1 output file written: logo.1
Transcript written on logo.log.

$ ls
logo.mp    logo.1    logo.log

拡張子が.epsのファイルがありませんが、logo.1というのがMetaPostによって生成されたEPSファイルです。 中身を見ると、以下のように、お馴染みのPostScriptコマンドとPostScriptコメント(%%のスタイルは正確にはDSCコメントといいます)がならんだEPSファイルであることがわかります。

%!PS-Adobe-3.0 EPSF-3.0
%%BoundingBox: 0 -1 114 94
%%HiResBoundingBox: 0.00034 -0.00009 113.16754 93.72906
%%Creator: MetaPost 2.00
%%CreationDate: 2018.12.04:1520
%%Pages: 1
%%BeginProlog
%%EndProlog
%%Page: 1 1
 0.6 0.6 0.6 setrgbcolor
newpath 26.8672 14.0469 moveto
26.91411 11.4531 27.3867 8.51172 26.0273 6.51953 curveto
23.89839 3.40234 19.0938 1.85156 15.0313 1.77734 curveto
8.3789 1.66016 2.88672 6.95703 2.76563 13.6133 curveto
2.64453 20.2695 7.94531 25.7617 14.5977 25.8789 curveto
21.2539 26 26.7461 20.7031 26.8672 14.0469 curveto
 closepath fill
 0 0 0 setrgbcolor
newpath 111.97699 17.8125 moveto
110.96901 18.2617 109.785 17.8047 109.34 16.7969 curveto
109.188 16.4453 lineto
109.188 16.4453 109.141 16.3398 109.047 16.1328 curveto

(つづく)

この「MetaPostが生成したEPS」こそが、Purified EPSの正体です。 残念ながらPurified EPSの明確な仕様はよくわからないのですが、MetaPostのマニュアルで「Purified EPS」と名づけられていることから、「MetaPostが生成したEPSがPurified EPS」だと考えて問題ないでしょう。

PostScriptとして見れば、Purified EPSはフルのEPSから機能を削り取ったDSCであり、EPSファイルのサブセットといえます。 したがって、通常のEPSと同じように扱えます。

さらに特筆すべき特徴として、Purified EPSは、pdfTeX(およびLuaTeX)やdvipdfmxに完全な処理系が実装されています。 これが何を意味するかというと、「TeXエンジンにPostScriptの処理系が実装されていないのでEPSファイルは使うべきではない」というベストプラクティスが、Purified EPSには適用されないのです。 Purified EPSは、むしろTeXのエコシステムで積極的に使ってよい形式であるとさえいえます。

f:id:golden-lucky:20181204123124p:plain:w400
EPSがMetaPost由来であれば、TeXエンジンが直接処理できる

ちなみに、dvipdfmxに至っては、その名称に反し、(DVIではなく)生のPurified EPSを受け取ってPDFに変換するオプションまで用意されています。

$ dvipdfmx -M logo.1  # dvipdfmxに、DVIファイルではなくEPSファイル(ただしPurified)を渡せる!
logo.1 -> logo.1.pdf

4105 bytes written

ここまでのまとめ

ここまでの話をまとめましょう。

  1. 一般のEPSファイルは、完全なPostScriptの処理系を持たないTeXのエコシステムでは直接扱えないので、必要な場合には外部アプリケーションであるGhostscriptをそのつど起動して処理を投げている
  2. あらかじめEPSファイルをPDFにしておけば、現代的なTeXのエコシステムはPDFを直接扱えるので、そのつどGhostscriptが起動されることはなくなる(これが現在のTeXワークフローの主流)
  3. しかし、テキストファイルであるEPSファイルには、PDFにない利点もある
  4. ところで、MetaPostが生成するEPS(Purified EPS)でれば、現代的なTeXのエコシステムに完全な処理系が内蔵されている

このように状況を整理すると、そのつどGhostscriptが起動されるというハンデを負わずに、テキストファイルとしての扱いやすさだけを享受できる、そんな作業フローの可能性があることに気づきます。 そう、一般のEPSファイルを、可能な場合にはPurified EPSに変換してしまえばいいのです。

一般のEPSをPurified EPSに変換する

ずばり、purifyepsというツールがあります。その名のとおり、EPSファイルをpurifyしてくれるユーティリティです。 TeX Liveに入っているので、ふつうにTeX環境をインストールしていれば利用可能です(参考)。

このpurifyeps、実態は、pstoeditmpostという別々のツールを順番に呼び出すだけのPerlスクリプトです。 前段のpstoeditが、EPSファイルをMetaPostのソースファイルに変換してくれます。 後段のmpostは、MetaPostの処理系そのものです。 前段のpstoeditで生成されたMetaPostのソースファイルを、後段のmpostにかけて、Purified EPSが出力されるという寸法です。

f:id:golden-lucky:20181204123553p:plain:w500
purifyepsにより通常のEPSをMetaPost由来にできる場合がある

purifyepsを使うには、pstoeditにフォントマップを指定して、もとのEPSファイルで使われているフォントをmpostが知っているフォント名に置き換えてあげる必要があります。 これはmpost.fmpとして、かなり長大なファイルがpstoeditに付随してくるはずです。 しかし、このデフォルトのmpost.fmpに登録されていないフォントがEPSファイルで使われていたり、対応するtfmを自分の環境に持っていなかったりすると、変換がコケます。 その場合は、エラーメッセージをもとに、PostScriptフォント名と対応するtfm名からなる行をmpost.fmpに追記してあげれば、とりあえず通ります。 対応するtfm名は、ぶっちゃけなんでもいいので、とりあえずcmr10でも指定しておいて変換を通してみましょう。

あと、ちょっと罠があって、素のpurifyepsだとGhostscriptが意味不明なエラーを吐き出す可能性があります。 これは、pstoeditでGhostscriptのDELAYBINDオプションが暗に指定されているからです。 DELAYBINDは、PostScriptのbindコマンドの動作をちょっと変えることで、標準ライブラリのコマンド名を上書きしているような場合でも問題なく扱えるようにするためのGhostscript独自の仕掛けです(参考)。 しかし、Adobe Distillerが隠し持っているinternaldict辞書を操作するsuperexecというコマンドがsystemdictにあったのを取り除いたときに、副作用があるので取り除かれてしまいました(参考)。 結果として、Adobe Illustratorなどで生成されたEPSの多くは、素のpstoedit(したがってpurifyeps)で変換しようとすると意味不明なPostScriptエラーを吐き出して終了するわけです。

pstoeditは、DELAYBINDを無効にするGhostscriptの-dNOBINDオプションを有効にするために、-nbというオプションを指定してあげます。 しかし、これをpurifyepsから実現する手段はありません。 そのため、purifyepsのソースでpstoeditを呼び出している箇所に直接-nbオプションを書き加えるしかなさそうです。 以下のようにpstoeditを呼び出している箇所があるはずなので、そこに-nbオプションを追記しましょう。

(省略)

# Utilize pstoedit to convert from EPS to MetaPost.
my $mpfile = $tempbase . ".mp";
executeCommand "pstoedit -ssp -mergetext -f mpost -fontmap \"$fontmap\" \"$infile\" \"$mpfile\""; 

(省略)

Perlスクリプトなので、パスが通っているディレクトリにあるpurifyepsというファイルを直接いじってしまえば目的は達成されます。

Purified EPSをTeXに書き換える

こうして変換できたPurified EPSは、\includegraphicで読み込んでも、Ghostscriptが起動することもなくpdfTeX/LuaTeXやdvipdfmxだけでPDFの画像になります。 変換前の一般のEPSファイルを読み込んだときに比べて、爆速で処理が終わります。

ここで満足してもいいんですが、ここでさらに、Purified EPSをTeXソースそのものに変換できないか考えてみましょう。

なんでそんなことを考えるかというと、画像を外部ファイルではなくTeXソースで持っていることには、次のようなメリットが考えられるからです。

  • 本文と同じスキームで図中のフォントを扱える(数式日本語も)
  • 図中の長文を組版してもらえる(数式日本語も)
  • 図中で脚注や相互参照や索引が使える

つまり、メリットしかない。 補足しておくと、実はここまでの話はすべて図中のフォントがASCIIであることを前提としていて、EPSファイル中で日本語が使われているとpurifyepsが日本語をアウトライン化してしまうのでした。 もしTeXソースにできてしまえば、日本語が使えるように設定したTeXで処理するだけで図中に日本語を埋め込めます。

幸い、最近のTeXには、PGFという高度なベクター画像のための仕組みがあります。 PGFは、人間が書くときにはTikZという独自のインターフェースを使うのですが、PGFそのものはかなりPostScriptに近いシンタックスであり、いかにも直接変換できそうな雰囲気がします(参考)。 これを利用すれば、Purified EPSをTeXのマクロにできそうです。

同じことを考える人はいるもので、すでにeps2pgfというJava製の変換ツールを作っている人が過去にいました(参考)。 現在はSourceforgeにポストされているもののソースもない状態で、メンテもされてなさそうなんですが、とりあえずダウンロードして使ってみるとそれなりに満足のいく結果が得られました。

$ java -jar eps2pgf.jar logo.1 -o logo.tex

結果として生成されるTeXファイルは、 pgfpicture 環境にPGFのコマンドが並んだこんな状態です。

% Created by Eps2pgf 0.7.0 (build on 2008-08-24) on Tue Dec 04 15:38:00 JST 2018
\begin{pgfpicture}
\pgfpathmoveto{\pgfqpoint{0cm}{-0cm}}
\pgfpathlineto{\pgfqpoint{3.992cm}{-0cm}}
\pgfpathlineto{\pgfqpoint{3.992cm}{3.307cm}}
\pgfpathlineto{\pgfqpoint{0cm}{3.307cm}}
\pgfpathclose
\pgfusepath{clip}
\definecolor{eps2pgf_color}{rgb}{0.6,0.6,0.6}\pgfsetstrokecolor{eps2pgf_color}\pgfsetfillcolor{eps2pgf_color}
\pgfpathmoveto{\pgfqpoint{0.948cm}{0.496cm}}
\pgfpathcurveto{\pgfqpoint{0.949cm}{0.404cm}}{\pgfqpoint{0.966cm}{0.3cm}}{\pgfqpoint{0.918cm}{0.23cm}}
\pgfpathcurveto{\pgfqpoint{0.843cm}{0.12cm}}{\pgfqpoint{0.674cm}{0.065cm}}{\pgfqpoint{0.53cm}{0.063cm}}
\pgfpathcurveto{\pgfqpoint{0.296cm}{0.059cm}}{\pgfqpoint{0.102cm}{0.245cm}}{\pgfqpoint{0.098cm}{0.48cm}}
\pgfpathcurveto{\pgfqpoint{0.093cm}{0.715cm}}{\pgfqpoint{0.28cm}{0.909cm}}{\pgfqpoint{0.515cm}{0.913cm}}
\pgfpathcurveto{\pgfqpoint{0.75cm}{0.917cm}}{\pgfqpoint{0.944cm}{0.73cm}}{\pgfqpoint{0.948cm}{0.496cm}}
\pgfpathclose
\pgfusepath{fill}
\definecolor{eps2pgf_color}{rgb}{0,0,0}\pgfsetstrokecolor{eps2pgf_color}\pgfsetfillcolor{eps2pgf_color}
\pgfpathmoveto{\pgfqpoint{3.95cm}{0.628cm}}
\pgfpathcurveto{\pgfqpoint{3.915cm}{0.644cm}}{\pgfqpoint{3.873cm}{0.628cm}}{\pgfqpoint{3.857cm}{0.593cm}}

(つづく)

これはもう完全にTeXなので、uplatexでもpdflatexでも好きなものでPDFにすればいいでしょう。 もちろん、ふつうにTeXソースとして編集できるので、LaTeXのパッケージの機能もフルに使えます。

% Created by Eps2pgf 0.7.0 (build on 2008-08-24) on Tue Dec 04 15:38:00 JST 2018
\begin{pgfpicture}
\pgfpathmoveto{\pgfqpoint{0cm}{-0cm}}
\pgfpathlineto{\pgfqpoint{4cm}{-0cm}}
\pgfpathlineto{\pgfqpoint{4cm}{4.0cm}}
\pgfpathlineto{\pgfqpoint{0cm}{4.0cm}}
\pgfpathclose
\pgfusepath{clip}
% 追記
\pgftext[x=2cm,y=3.7cm,rotate=0]{%
  \fontsize{12}{12}\selectfont\parbox{4cm}{\centering ラムダノート}}
\pgftext[x=2.12cm,y=2.5cm,rotate=-65]{%
  \fontsize{18}{10}\selectfont\scsnowman[hat=true,muffler=red,arms=true]}
\pgftext[x=3.73cm,y=0.95cm,rotate=80]{%
  \fontsize{18}{10}\selectfont\scsnowman[hat=true,muffler=red,arms=true]}
% ここまで追記
\definecolor{eps2pgf_color}{rgb}{0.6,0.6,0.6}\pgfsetstrokecolor{eps2pgf_color}\pgfsetfillcolor{eps2pgf_color}
\pgfpathmoveto{\pgfqpoint{0.948cm}{0.496cm}}
\pgfpathcurveto{\pgfqpoint{0.949cm}{0.404cm}}{\pgfqpoint{0.966cm}{0.3cm}}{\pgfqpoint{0.918cm}{0.23cm}}
\pgfpathcurveto{\pgfqpoint{0.843cm}{0.12cm}}{\pgfqpoint{0.674cm}{0.065cm}}{\pgfqpoint{0.53cm}{0.063cm}}
\pgfpathcurveto{\pgfqpoint{0.296cm}{0.059cm}}{\pgfqpoint{0.102cm}{0.245cm}}{\pgfqpoint{0.098cm}{0.48cm}}
\pgfpathcurveto{\pgfqpoint{0.093cm}{0.715cm}}{\pgfqpoint{0.28cm}{0.909cm}}{\pgfqpoint{0.515cm}{0.913cm}}
\pgfpathcurveto{\pgfqpoint{0.75cm}{0.917cm}}{\pgfqpoint{0.944cm}{0.73cm}}{\pgfqpoint{0.948cm}{0.496cm}}
\pgfpathclose
\pgfusepath{fill}
\definecolor{eps2pgf_color}{rgb}{0,0,0}\pgfsetstrokecolor{eps2pgf_color}\pgfsetfillcolor{eps2pgf_color}
\pgfpathmoveto{\pgfqpoint{3.95cm}{0.628cm}}
\pgfpathcurveto{\pgfqpoint{3.915cm}{0.644cm}}{\pgfqpoint{3.873cm}{0.628cm}}{\pgfqpoint{3.857cm}{0.593cm}}

(つづく)

f:id:golden-lucky:20181204161246p:plain
ラムダノートのロゴ with ☃

以上で、PostScriptをある程度読めてベジエを手で調整する気力とフォントの位置を微調整する忍耐力がある人にとっては、実に快適なテキストベースの図環境が得られることがわかりました。

f:id:golden-lucky:20181204123556p:plain:w600
EPSをMetaPost経由でTeXソースにする

まとめ

MetaPost、そのまま作図のためのツールとして使うにはハードルが高いんですが、画像編集ソフトで生成したEPSファイルを本稿の手法でTeXにするための中間形式としては魅力がありますね。

ただし、その変換パスにも、pstoeditという形でGhostscriptが関与します。TeXはGhostscriptから離れては生きていけないのです。

ところでラムダノートという会社では、「古いEPSを活用してドキュメントを作りたい」といったお仕事も、こんな感じでわりと効率よく引き受けることが可能です。 著者や原文のソースにあるメタ情報をフル活用したい、バージョン管理したい、ミスが怖いので人力で変換したくない、といったドキュメント関連のお悩みがありましたら、下記のフォームよりご連絡ください。

www.lambdanote.com