投稿者: Jun Mukai

git switchを使う

どこで読んだかわすれたが、git switchgit restoreの話の記事を少し前に読んだ。

git checkoutは、説明されれば理解はできるが初心者には非常にわかりづらいコマンドのひとつで、「gitは難しい」「わかりづらい」「UIが悪い」といった風評の源のひとつなんじゃないかと個人的には思ってる。でもまあ使っていればなれるので、これまで特に疑問もなく使ってた。が、git switchとrestoreの導入により「checkoutが難しい」(というか、知らないと「この作業をしたいときにどのサブコマンドで実現できるのか」がわからない)という問題は発展的解消を遂げたといえる。よかったね。

よかったね、で終わってしまえばよく、そのまましばらくgit checkoutを使っていたのだけど、こういうのはわかりやすいほうになれるべきなんじゃないかとあるときふと思って、もっぱらgit switchを使うようにしてみた。

最初のステップはとりあえず使うことを心がけるのみだったが、すぐ限界がきた。git checkoutは手元ではgit coというエイリアスで使えるようになってて手軽だし、指が覚えていてつい打ってしまいがち。とくにmainに戻るときとかはgit co mainまで一瞬で打ってしまって気づいたときにはもう遅い。べつに遅くてもいいんだけど、矯正のためにcoというエイリアスを削除してみた。いまだにgit coをやってしまってエラーメッセージをみることがよくあるけど、次第に慣れてきたように思う。

switchのかわりにgit swというエイリアスを作るというのを教えてもらったのでこれも設定してみたが、どうも自分の運指には向いていないようでほとんど使っていない。switchとすべてタイプしてしまうか、swiぐらいまで打ったところでタブ補完してしまうことが多くなった。

(余談だが、10年以上前に同僚にとてもタイピングが速い人がいて、なにかの折にその人になにかのコマンドの使い方を教えてもらいに行ったら、タイピングも速いし正確なのでタブ補完を一切使ってなくて、なおかつ私のタブ補完使いまくりよりも速く入力できていて感心したということがあった。自分はtypoも多いしタブ補完がないと生産性は保てないけど、真にタイピングが上手な人はタブ補完を使わないものなのかもしれない)

まだ移行して日が浅いのでrestoreのほうはほぼ使っていないが、いずれ使う日もくることだろう。

いちおう念のために補足しておくと、移行してみたかったのは上にも書いたように個人的になんとなくそう思ったから、というものであり、広く移行を推奨するわけではありません。どっちでもよいと思う。

“Snow Crash” と30年目のMetaverse

ここ最近またMetaverse (メタヴァース)という言葉が話題になってて、もう何度目かとおもいつつ、いい機会かもしれないと思って Neal Stephenson の “Snow Crash” を読んだ。

“Snow Crash” は1992年に発表されたSF小説だが、作中のオンライン世界の名前がMetaverse。本作はとくにアメリカのハッカー・ソフトウェア業界では有名で、多くの人が読み影響を受けたことを表明している。Metaverseが話題になったこともこれまで何度もあった。邦訳も1998年に出ていて、ぼくは2001年にでなおしたハヤカワ文庫版を持ってたんだが、なんとなくずっと読みそびれていたのだが、ようやく重い腰を上げて読んでみたというわけ。邦訳版は渡米のときに手放してしまったので、今回は原語で読んだ。

作品そのものについて

あらためて読んでみたら、これが面白かった。意外というかなんというか、わりと典型的なサイバーパンクっぽさがあるし、現代アメリカを戯画化したような世界設定(たとえばフランチャイズが支配・統治する区画に分かれている)のことも、渡米してみることで実感されることがあった。というわけで今更だけど読めてよかったと思う。SF設定もなかなかおもしろく(しかしこれだけアジアの影響が色濃い作品なのに聖書ネタで来るんだなあ)、楽しめた。

ただどうしても書かれた時代というのは鼻につく面はある。日本の存在感が強い(Nipponeseと言われている)のもそうだし、ソフトウェアは工場労働者のように生産されるようになってしまってハッカーの存在感が消えてしまい、主人公はピザ配達をしているという序盤の設定も、正直なところ時代を感じる。そもそも snow crash という言葉自体も、ソフトウェアがバグで止まってしまって意味のないランダムな画像を出しているのを表現したものだというのが時代を感じるよね、現代なら青い画面(blue screen of death)になっているだろうなあ。

(心底どうでもいい雑談だが、Neuromancerの冒頭の有名な「港の空の色は空きチャンネルに合わせたTVの色だった」は、本来は灰色の曇り空のことだったが、青い画面が出ることが多いので青空を意味していると解釈され、もともとギブスンはそういう意図で書いていたという話も広まっているらしいね)

というわけで、それなりに同時代性のある作品であるわけで、あらためて読む必要があるとか、今読んでも褪せないとかは、さすがに言いづらいところはある。楽しく読める面はあるのだけどね。

Metaverseについて

で、Metaverse。これも正直なところ、今読んでもこれが当時どれだけのインパクトがあったのかを想像するのがむずかしい。Metaverseはもう現実にとっくに追い越されているので、ふつうに読むと「フーン」といった感想しか生まれないだろう。

Metaverseは端的に言うと3Dのバーチャルリアリティなインターネットだ。VR chatだといってもいいし、Second Lifeだといっても間違いではない。ヘッドマウントディスプレイを装着して閲覧できる仮想空間。自作のアバターでうろちょろできる。いろんな人が自分の世界を構築できてワイワイできる。

これについて2021年の今から考える際には、1992年という時代を想像しておく必要があると思う。商用インターネットというものはなかった。オンラインのコミュニケーションはアメリカならCompuserveはあったけど物好きだけのものだった。というふうに考えれば、ほとんど誰でも参加できてオンラインコミュニケーションがとれる現代のネット・ウェブ的な世界が、けっこう「まとも」に描いているのは興味深いようにも思える。というよりは、こういうSF小説(たち)の想像力が、現代のオンラインコミュニケーションの作り手たちに影響を及ぼしたというべきだろうか。

もちろん現代ではVRはそこまで普及していない。VR chatもあるし、Second Lifeもあるけれど、Metaverseの描かれ方はもっとウェブ的なオープンな世界で、誰でもサーバを立てたりできそうな世界に思えて、そこは(VRMLとかあったけど)まだ来ていないとも言える。複数のサーバにまたがった世界で個人は同じアバターを継続的に使う(使わないといけない)シングルサインオンで単一IDな世界でもあり、そこも実現されていないけれど、これは実現されないほうが良かった話なような気もするかな。

というわけで、読んでみて改めて感じるのは、2021年にもなっていまさらMetaverseが話題になってるのはヤバいなということだ。まあVRということをかっこよく言いたいというだけなのかもしれないが(Metaverseという単語のかっこよさ、というのはこれが最初に流行ったときの重要な要素ではあるはず)。

結論

Snow Crash は90年代のハッカーコミュニティに多大なる影響を与えたのは間違いないし、Metaverseはいまもって奇妙なバズワードとして存在している。でもそういう文脈のために読む価値は、Snow Crashにはもうないと僕は思う。作中のビジョンはもう実現されてしまっている。でもまあ当時はやっていたサイバーパンクの作品群のひとつとして、SFとして、ふつうに面白いとは思う。

9月に読んだ漫画

先月同様に主にタイトルのみ。67冊。多かったな。ジョジョリオンを一気読みしたのが大きい。

『SHIORI EXPERIENCE』7-17

先月からつづきで読んでた漫画。まあまあ。

『ウィッチウォッチ』2

『SAKAMOTO DAYS』3

『ルックバック』

『白井カイウ✕出水ぽすか短編集』

『ハイパーインフレーション』2

ハイパーインフレーション、1巻ではピンとこなかったけどなんか面白い気がしてきた。

『1日外出録ハンチョウ』12

『機動戦士ガンダム サンダーボルト』8-18

これも溜まってたのを一気読み。面白い。すでに知っていたけど腱鞘炎の悪化でいきなり絵柄がかわるのはびびるな。

『青の花 器の森』8

『ダンジョン飯』11

『生き残った6人によると』1

『夏を知らない子どもたち』

『星あかりグラフィクス』1-3

『神客万来』3

『ジョジョリオン』1-27

完結したので一気読みした。が、まあ……一気読みするもんじゃないなと思った。最後はグダグダだったし、テーマが10年たつとこう、時代性を失っている。

『ブルーピリオド』11

『はじめてのひと』6

谷川史子はどれを読んでもけっきょく同じような話になってしまう。だが、それがいいのだ。

『邦キチ!映子さん Season 6』

ネットで公開された当時は読めなかったシン・エヴァンゲリオンの回が読めて良かった。

『スポットライト』3

シェルスクリプトの代替

要約:決定版はとくにない。

kzys氏のシェルスクリプトを書かないという記事は面白かった。 https://blog.8-p.info/ja/2021/09/15/bash/

シェルスクリプト、ごくたまに書くことはあるが、ほんともう細かい話とかはすべて忘れているし、覚える価値を感じない。いまさら覚える必要のない技術だなと感じる。が、その一方でなかなか代替品がないようなニッチでもある。

自分は必要に応じてPythonかRubyか、といったあたりを使うことが多いが(perlはもう書けなくなった)、なかなかこれという感じには思い至らない。なにがいいんだろうね?という。

前提条件:インタラクティブな環境(REPL)はなくてもいい。そこはもう既存のシェルでいい。自動化したシェルスクリプト的なタスク記述を目標とする。bashの置き換えという意味では「どこにでもインストールされていることを前提にしないといけない」という問題もあるが、そこも問わない。自分が個人的に、またはチーム内の人に使ってもらうぐらいの規模で考えるので、コマンド一発でインストールできるか、コンテナイメージで実行できるぐらいであればもう良い。それ以上のことは検討しない。bashには勝てません、で終わってしまうので。

Python / Ruby

こうした言語をそのまま使うというもの。GoogleではPythonがよく使われていると思う。たとえばChromiumのビルドシステムGNでは補助スクリプトはすべてPythonで書くことになっている。ちょっとした処理をしてフラグを生成したものを渡してコマンドを起動して、その結果をちょっと整形して、といったことをやるには気軽だ。もっと気軽には自分はRubyで書いたりもする。

欠点。まずコマンド起動の部分が関数呼び出しになるのでまあまあ面倒くさい。パイプしたりするのもちゃんと書くのはじゃっかん面倒。そのため、標準出力をそのまま文字列として変数に渡す、みたいなことをしがち。

言語内DSL

Rubyとかの言語機能を使って言語内DSLでなんとかしようという話。Rubyの shell.rb とか。https://ruby-doc.org/stdlib-2.6.3/libdoc/shell/rdoc/Shell.html PerlにもPythonにもいろいろある模様。

またはzxもこれと近いね。https://github.com/google/zx。zxはtagged template stringsを使ってるのがオシャレだな。

欠点。なんか言語機能のabuseっぽくてちょっと嫌。けっきょくスクリプト起動のための依存関係が増えて嬉しくない(shell.rbはその点、標準ライブラリに入ってるので楽。しかしこれを標準ライブラリに入れるのはすごいな)。コマンドと変数名がかぶるとおかしなことになる。パラメータの展開とかいろいろ不安を感じるものも多い。などかなあ。

ワークフローエンジンまたはmake

makeを使うという主張もあった。一種のワークフローエンジン・タスク管理ツールを使うという手もある。Python Invoke http://www.pyinvoke.org/ とかもそれに近いですね。タスクの順番や依存関係とかを簡潔に書ける。外部コマンド実行はpure Pythonなどよりはマシかな。そういや昔、そういうのを使うプロジェクトに関わったことあるなあ。

欠点:書きづらいタイプのタスクもけっこうある。条件分岐とかしだすと、まあできるんだけどとたんに面倒になったり。パイプやコマンドの出力結果の加工とかも考えるとMakeはそれ単体では成り立たない気がする。言語内ワークフローエンジンは言語の機能を使えばまあアリではあると思う。

独自スクリプト言語

上記の欠点をあげつらうならもういっそ独自言語でもいいのかな、という気もする。PowerShell https://docs.microsoft.com/en-us/powershell/ とか、Oil https://www.oilshell.org/ とかのアプローチ。外部コマンド実行やパイプ、典型的な構造データ(JSONなど)の処理は言語処理系内でできるようにすればいい。

欠点:新しい言語や処理系を覚えないといけない。JSONの処理がたとえばビルトインでできても、やっぱりYAMLやXML、TOMLも扱いたいです、みたいになったときに困るかもしれない。

結語

というわけで、これが絶対いいっていうのはないんだよな、と思う。個人的には言語内DSLがいいかなあと思うが、その出来にもよるしね。

8月に読んだ漫画

ふとまとめてみる。計41冊か、思ったより読んでるな。気が向いたものだけ軽くコメント。

『兎〜野生の闘牌〜』 1-9巻

なんとなくネットでみかけた麻雀漫画の古典。自分が高校生のときにも友達が勧めてた気がする。まあまあ面白いんだけど、根本的に自分は麻雀に興味がなさすぎなんだよな。じゃあなんで読んだのかっていう話なんですが……。

『Thisコミュニケーション』4

あいかわらずひどい話で面白かった。デルウハが本名デ・ルーハだったのはぜんぜんわかってなかった。作者のイントネーションの感覚、特異では。

『マッシュル』7

『鴨乃橋ロンの禁断推理』3

『Dr. STONE』22

最終巻なのか?というほどの盛り上がりの果ての展開がとても良かった。面白いなあ。

『僕のヒーローアカデミア』31

昨今の展開が広がりすぎて読んでてもよくわからなくなってきたので、一段落してくれて助かる。でもまあ惰性で読んでる感はある。

『逃げ上手の若君』2

最初どうなのかなと思ったけど、松井優征はやっぱり上手いなと思わせるものがある。

『おとなりに銀河』2

『二月の勝者』12

『転がる姉妹』2

第1話のインパクトがすごかったけど出オチじゃなくて普通に面白く続いていてよい。

『最果てから徒歩5分』2

『愚者の星』6

『ほしとんで』1-5

ふと見かけた俳句漫画。まあまあ面白いかなと思いながら読んでいたら唐突に打ち切りっぽい終わり方で終わって驚いた。

『猫奥』3

猫漫画。とても良いです。

『定額制夫のこづかい万歳』3

『望郷太郎』5

『相続探偵』1-2

『海が走るエンドロール』1

夫が亡くなったおばあちゃんが突然、美大に入って映画製作しはじめるというまんが。面白い。

『東独にいた』1

『太陽と月の鋼』3

『SHIORI EXPERIENCE』1-6

地味な高校教師に突然ジミヘンの幽霊が乗り移って……という漫画。漫画として達者でけっこう楽しめる。

『地図にない場所』1-2

安藤ゆき、かなり好きなのに新連載はじまってたの気づいてなかった。教えてよ! 本作も面白いけど、今のところは過去作のほうが好きかな。今後に期待。

転職した

Facebookなどではステータス更新を済ませたが、6月末にグーグルをやめて新しくスタートアップに転職した。すでにそのスタートアップで働いて、いま2週間くらいというところ。

グーグルには13年以上勤務した。いろんな面でずいぶんお世話になりました。自分の意思でやめたけど、実際のところ、とくにすごく嫌なことがあってやめた、というわけではない。ややこしいこととかが皆無というわけではないけれど、あの規模の会社としてはずいぶん頑張っているだろうし、総合的にとてもいい会社だと思う。部下のいないヒラエンジニアとしても待遇もよかった。やっていたプロジェクトも意外と(?)インパクトのある製品だったし。

今回は、たまたま自分にとって絶妙なタイミングでお声がけいただいたのと、こういうシリコンバレーのスタートアップに入るのは得がたい経験だろうしいい機会かもしれないなと思った、という次第です。

新しい会社は、まだ入って2週間なのでなんとも言えないけれど、いまのところは楽しくやっている。フルリモートの会社なので在宅勤務を続けていて、身の回りの環境の変化はあんまりない。この転職の都合による引っ越し等も必要ない。ミーティングが激減したので会話量が減ったかなあというのが一番大きな変化かなぁ。

そんなかんじです。今後ともよろしくおねがいします。

ところで新会社はまだぜんぜん小さな会社なんですが、今はエンジニア(とくにバックエンド系)を採用強化中とのことです。フルリモートな会社なので、日本からでも(たぶん)働けます。興味のある人はリンク先の下の方のフォームから、ぜひ応募してみてください。

アンディ・ウィアー “Project Hail Mary”

マット・デイモン主演で映画化された『火星の人』(原題:The Martian……映画の邦題は『オデッセイ』)のアンディ・ウィアー先生の新作長編、”Project Hail Mary” を読んだ。

面白かった!

主人公は記憶を失った状態で、機械に繋がれた生命維持装置のようなもののなかで目が覚める。ここはどこか、なぜ自分はここにいるのか? そもそも自分は誰なのか? それすらわからないなかで。同じく生命維持装置につながれているがなんらかの事情で死んでしまった二人の死体のほかは誰もいない、完全なるコンピュータ制御の部屋。少しずつ戻ってきた記憶と、様々な実験・観測から、人類存亡の危機と、Hail Mary計画の詳細が次第に明らかになってくる……といったあらすじ。

『火星の人』のような軽妙な語り口は健在で、読みやすいページターナー。それでいてけっこう予想しない方向に話が転がっていく。今回はSF設定もいろいろあって、描写が細かいがゆえに人によってはかえって「いやそれはさすがにそうはならんやろ」という感じになる気もするし、ちょっと雑な部分もあると思うが、個人的には楽しめた。

この面白さってなんだろう。『火星の人』もそうだったが、なんというか、科学に対する信頼感というか、未知の現象を科学で解き明かしていくことの楽しさというか、そういう部分があって、面白さがその土台の上に立っている感じがあるよね。ある意味では理系な人々に対する慰撫でしかないとも言えるんだが、でもやっぱりそこがいいんだよな。そういう面では、『Dr. STONE』とかと相通ずるようなところがある。

しらんけど人気あるだろうし、邦訳はすぐ出ることでしょう。お楽しみに。

F#でray tracing

先日F#に入門してみた。F#は.NET環境で動作する関数型言語で、OCamlの方言程度のものといった雑な認識だったんだけど、入門してみてその認識は改められた。OCamlとはぜんぜん違う、ML系の別個の言語だといっていい。

F#はML系列の言語だが、独自の構文規則があって、とくにインデントに意味をもたせているところがユニーク。これによって余計なセミコロンなどがかなり省略できるのは面白い。let … in 構文みたいなのもinなしで書かせられるし、またF#はOCamlと同じで、リストの区切りがセミコロンになっている([1; 2; 3] のように書く。[1, 2, 3] は [(1, 2, 3)] と同じと解釈され、タプル1つのリストとなる)が、リストの各要素で改行すれば要素区切りのセミコロンすら省略できる。

F#は.NETに標準添付されていて、特別なインストールを必要としないというのも面白い。.NETをインストールすれば インストールは完了している。dotnetコマンドでdotnet fsiを打てばreplも立ち上がる。

さて、入門といってチュートリアルドキュメントを適当にいくつか眺めただけではちょっと面白くない。練習がてらなんかしらのプログラムを書いてみたいところだけど、とくに書きたいプログラムのアテもない、というわけでRay tracing in a weekendというやつをF#でやってみた。もとのコードサンプルはC++で書かれているが、これをF#で書いてくことの面白さみたいなものもあるだろう。たぶん。

速度を出すことは目標ではないので遅いだろうし、実装もしょぼいとは思うが、書いたものは想定通りに動いて、the next weekまで修了したので目標を達成できたとおもう。じつはray tracing実装は初めてだったので、そこもまぁ良かった。

で、書いてみることでいろんな言語機能を使ってみたりして楽しく、F#の良さも楽しめたと思う。F#はOCamlと違ってオペレータのオーバロードもできるので、ベクトル同士の足し算とかを + で書けるように定義できたりしてラクだったりした。

一方書いていてこれはいまいちだなあと思ったところもいくつかあった。最大のものは、意外とオブジェクトが相手だと型推論がちゃんとしてくれないこと。具体的には、たとえば乱数オブジェクトから乱数を引いてランダムな3次元ベクトルをつくる関数、

let random_vec rnd = Vec3(rnd.NextDouble(), rnd.NextDouble(), rnd.NextDouble())

みたいなのを書いてみたとすると、

error FS0072: Lookup on object of indeterminate type based on information prior to this program point. A type annotation may be needed prior to this program point to constrain the type of the object. This may allow the lookup to be resolved.

みたいなエラーが出てくる。適当に型推論してくれることはなく、オブジェクトを引数に取る場合はかならず型を指定しないといけない。

let random_vec (rnd: Random) = new Vec3(rnd.NextDouble(), rnd.NextDouble(), rnd.NextDouble())

これはちょっといけてない。レコード型などほかの型なら推論してくれるんだけど……。ちなみにOCamlはこういうのの型推論もよろしくやってくれる(NextDoubleというメソッドを持つオブジェクトなら良い、みたいに推論してくれる)。これは言語そのものというより、バックエンドである.NETのオブジェクトの構造を反映しているからだろう。ほかにもインタフェースにキャストしてあげたりといった面倒が意外とあった。

まぁでも久々に関数型言語で遊べてそれなりに楽しかったな。また何か使う機会をうかがいたいところ。

移転したコーヒー屋に行ったらデルモンテ工場跡地があった

好きでよく通っていたChromatic Coffeeというコーヒー屋が移転していた。この移転の話もこれはこれでちょっとややこしく、変な話なのだが、この記事の本筋とは関係ないのでひとまずおくとして、移転したのだ。

あたらしい場所はサンノゼのCalTrain駅からやや南にいったところ、正直あんまり足を踏み入れたことのない場所だ。治安がわるいとかではまったくないだろうが、shelter-in-place(自宅待機命令)の関係で営業しているのかもよくわからない。コーヒー豆は通販できるのでまったく営業していないわけじゃないだろうが、よくわからん。

というわけで行ってみた。8月頭のことなのでちょっと前のことだけど。

行ってみたらコーヒー屋が入居していたのは工場跡地を改装したようなノコギリ状の建物だった。周囲は大きなアパートが多い。もともと工場があったエリアの再開発地域なのだろう。ほかの入居店舗は自転車屋や氷屋、ビアバーなど。コーヒー屋としては営業はしていて注文もできるが、店内での飲食はもちろんできない。豆のローストはこの場所でやっているようだ。そこでコーヒー豆を一袋買い、ついでに妻と飲み物を一杯ずつ頼んだのだが、さてどうしたものか。駐車場の日陰で立ちのみをしている人もいるけれど、とおもって携帯電話の地図を見てみるとすぐ近くに公園があるっぽいので、そこにいってみることにしたのだ。

さてそのDel Monte公園という公園についてみたのだけど、道のむこうに見覚えのあるロゴがある。思わず二度見したが間違いなくデルモンテだ。デルモンテっていえばなんだろう。ケチャップか、トマトジュース? あれ。あのデルモンテのロゴだ。なんか貯水タンクみたいなやつにでかでかと描いてある。

IMG_20200802_155540

かなり近寄って撮った写真なので角度の関係でロゴがとても分かりづらいけど、よーくみるとおなじみのデルモンテのロゴが見て取れると思う。

デルモンテは日本だとキッコーマン傘下のブランドだが、もとはカリフォルニアの会社だ。もともとモントレー(シリコンバレーから南に行ったところにある街。今は水族館で有名)の近くに、かつてデルモンテホテルというリゾートホテルがあった。このホテルに卸す食料品を扱うということで(当時の)有名リゾートホテルの名前をつけたブランドがデルモンテなんだそうだ。そういう事情があるから、モントレーとサンノゼはそれなりに離れているとはいえ、関連する施設があってもおかしくはない。おかしくはないが気にはなる。

コーヒー片手に周辺を散策したら、史跡を紹介するパネルが道端に設置されていた。それによると、たしかにここはもともとはデルモンテの缶詰工場でフルーツ缶詰とかを作っていたものらしい。人口の多いサンノゼ近郊であり、線路の近くという地の利もあったため、けっこう大規模な工場があったのだそうだ。はじまったのは1893年(19世紀!)で、それが1999年まで工場として残っていたというから、けっこう最近だ。シリコンバレーともてはやされてドットコムバブルで湧いていたころ、いっぽうこのへんには缶詰工場があったりしていたのだ。

もちろん「けっこう最近」といっても20年以上前のことである。工場の跡地はやがて再開発され、いまではアパートになったり公園になったりしている。アパートになっているが、いわれてみれば工場跡の面影が残る構造だ。妙に広々とした入り口の道、意味もなく立ち並ぶコンクリの柱や建物の感じ。そしてなぞのデルモンテのロゴ入りタンク。

IMG_20200802_161521

公園もデルモンテの名を残しているだけじゃなくて、路肩の構造物が果物のかたちをしていたりして名残りがある。

コーヒー屋を追っかけに来ただけだったのだけど、なかなかおもしろいものを見た。近隣住民のみなさんもいっぺん行ってみると面白いと思う。

zig言語でtomlパーサを書いてみた

数ヶ月前にzig言語というやつの存在を知って、これはちょっと面白そうだなと思ったので、勉強がてらなにかやってみよう、と思っていた。ある日、tomlのパーサはどうだろうかと思い立ってしばらくやっていて(Ghost of Tsushima で作業が中断したりしつつ)、まあまあ出来上がってきたと思うので、現在のところのソースコードをgithubに置いておいた(https://github.com/jmuk/zig-toml)。

というわけで、zig言語をちょっと書いてみた感想を残しておく。なお、利用したのはzig 0.6.0なので、今後いろいろ変わってくる可能性もあることは強調しておきたい。

zig言語のよいところ・興味深いところ

型の扱いがzigでは興味深いところだった。zigはかなりいろんなところでcomptimeというマーカをつけてコンパイル時にコンパイラが事前処理をするようなことができる。これでたとえば、C言語であればマクロ展開させるようなコードも自然に書けるしわかりやすい。

さらに型もコンパイル時の値として扱うことができ、型によって処理を変えるような関数もかんたんに書ける。さらには、型を受け取って型を返す関数によってテンプレート・総称型を実現している。たとえば、

fn List(comptime T: type) type {
    return struct {
        items: []T,
        len: usize,
    };
}

のように書ける。これはちょっとかっこいい。型レベルの情報(たとえば構造体のフィールド名など)はコンパイル時には展開できて、ちょっとしたリフレクション処理のようなこともできる。リフレクションはコンパイル時にしか発生しないので、実行コードはおかしな遅さにはならない。

また、エラーになりうる型、オプショナルな型などはいくつか特別扱いしている点も面白い。null安全性なども担保されているし、Go言語などと違い、エラーの場合にはエラーだけが返るようになっていてわかりやすいし、エラーが値でありながらtry構文などにより帯域脱出っぽいようにも書けるのもよさそう。

zigには(たぶんGoの影響で)defer文があるのだけど、errdeferという「エラーのときだけ実行される」処理というのが書けるのはかなり素晴らしいと思うというか、正直Goにもこれがほしい。これもエラーが特別な値でありエラーになりうる型を特別視しているところとうまく結びついている。

困ったところ、微妙なところ

メモリ管理はやっぱり大変。zigはGCのない手動メモリ管理言語であり、Rustのようなオーナーシップのようなものもない。C言語と同じでメモリ管理は手動で頑張る必要がある。どこでメモリがアロケートされ、誰がオーナーで、どの操作でオーナーシップが移りうるのか、といったことは言語レベルでいっさいサポートがない。こういう言語で書くのは久しぶりで、面倒さはやっぱりある(defer / errdefer でかなり軽減されているはずだが、それでも)。

メモリ管理については、zigはアロケータというオブジェクトが標準で定義されていて、好きなアロケータを扱えるようになっている。これはいい面もある。ある種のオブジェクトをひとまとめのアロケータで管理してまるごと破棄したり、その範囲を越えたアロケーションを禁止したりできるし、たとえば、既存のアロケータをラップしたリーク検知アロケータも標準から提供されていて、これを使ってテストコードを書けばコードのメモリリークはかなり容易に検出ができる(ただ、どこでリークしたかという情報までは取れないので修正の厄介さはそれほど軽減されていない気がするが……)。

一方これは、どのアロケータがどのオブジェクトのデータを割り当てたのかということがまったく自明でないことを意味する。たとえばツリーのようなデータ構造が不要になったので破棄したくなったとして、再帰的にノードをたどっていって順次廃棄していくというコードを書くだろう。でも、ノードによって異なるアロケータから割り当てられていたということも原理的にはありうるから、ノードごとにただしいアロケータに対して廃棄するような再帰処理を正しく書くのは、わりと自明ではない。

実際、標準のデータ構造でも、データ構造からアロケータへのポインタを持っていることが多い。リソース開放用のメソッドは deinit と呼ばれる関数で行われるが、このメソッドはパラメータを取らず、自分自身が参照するアロケータにリソースの開放を依頼するかたちになっている。私もこの構造に従ったし、それはそれで便利なんだが、なんだかありとあらゆるデータ構造がアロケータへの参照を持っているというのはいいことなんだろうかという疑問がないでもない。もう少しうまい仕組みはないものか。

defer / errdefer については自分がGo言語にひっぱられすぎている面もあるが、ちょっと戸惑ったところもあった。zigのほうがわかりやすいというか直感的ではあり、defer / errdefer はそのスコープを抜ける時にしか発動しない。その一方、

while (i < input.len) : (i+=1) {
  var v = Value{.Table = Table.init(self.allocator)};
  errdefer v.deinit();
  ....
  if (xxxx) {
    break;
  }
  results.append(v);
  if (yyyy) {
    return i+1;
  }
}
return ParseError.FailedToParse;

のように書いた時、このコードがエラーを返してもvが開放されることはないので困ったということもあった。関数全体としてはエラーだが、errdeferの設定したスコープではエラー終了していない(正常にbreakしている)ためにerrdeferは実行されない。breakのかわりにその場でreturnでエラーを返すとerrdeferが実行される。ちょっとわかりづらいと思った(zigに慣れていないだけかもしれない)。

ほかにも、標準の文字列リテラルは[]u8でしかないというのはけっこう戸惑いがあった。ちょうどいい文字列型のようなものは存在しないし、文字列リテラルからかんたんに利用する方法もない。std.ArrayList(u8)がわりと使えるが、ArrayListは対象の文字列をつねに所有しているので作るなら毎回コピーするしかない。使いやすいユーティリティ関数も少ないし、もうすこしできの良い文字列型がほしい。

また、先述したように総称型が「型を受け取り型を返すコンパイル時関数」なのはかっこいいんだけど、これは裏を返すと、ある型に対して「これはArrayListの型である」かどうか、というチェックは書けない。ArrayList自体はコンパイル時関数であって、いま手元にある型Tが、どのコンパイル時関数から生成されたものなのかなんてことはわからない。tomlパーサのようなものを書くとき、呼び出し側が指定したデータ型の構造に応じてパース結果を埋めていくみたいなことをしたいわけだけど、そういう理由により、ライブラリの定義する型をうまくあてはめるのはかんたんではないという印象を持った。

もうひとつ、エラー型が単純なenumなのは正直納得がいかない。tagged unionもありにしてほしい。パースエラーが発生した時、エラーの発生箇所を埋め込みたいんだけど、できない。パーサーオブジェクトに「最後のエラーの箇所」みたいなプロパティを与えるみたいな方法がありうるだろうけれど、どうしてもアドホックになってしまう。まあtagged unionにした場合、エラーオブジェクトをアロケートしている途中で発生するエラーみたいなややこしい問題がありうるだろうから、この設計方針はわからないでもないのだが、しかし不便じゃないか?

けっきょく、面倒なので ParseError.FailedToParse だけを返すコードになっていて、どこでエラーになったかは検出する方法がないようにした。そのほうが実装が楽だから……なんだけど、テストが失敗したときなどはわりとつらい目にあってしまった。

tomlについて

tomlは(主にCargo.tomlで)ちょっとだけ書いたことがあるくらいで仕様のことはあんまり詳しくなかったので、パーサを書くのはtomlの詳細についてもいい勉強にもなった。人間にとってわかりやすい仕様だしあんまり長くなく、複雑度が低くて良いというイメージであったが、人間にとってわかりやすい仕様は、パーサを書きやすい仕様ではないという学びもあった。

とくに面倒なのは重複するキーの禁止と、[]で設定するテーブルとインラインテーブル・インラインアレイで設定されるものの再定義が禁止されるという仕様。単純にキーに対してオブジェクトをトラバースしていくだけなら実装が単純なんだけど、このパターンは上書きになってだめ、このパターンはむしろ許される、みたいなルールがいくつもあって、けっこうわからない。仕様に書いてある例をテストケースに移して確認することにしたけど、当初の実装方針だとぜんぜんだめなパターンもけっこうあって難しかった。