一人一党党

一人一人の、一人一人による、一人一人のための政治制度を!

わたしがprintf()デバッグをする理由

現在、私はprintfデバッグに頼っているのに、
わたしがprintf()デバッグをする理由
http://freak-da.hatenablog.com/entry/20090325/p1
が半分どころか3/4ネタのようなので、真面目に言い訳してみた。

Coders at Work プログラミングの技をめぐる探求 | Peter Seibel, 青木 靖 |本 | 通販 | Amazon
https://www.amazon.co.jp/Coders-Work-%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E3%81%AE%E6%8A%80%E3%82%92%E3%82%81%E3%81%90%E3%82%8B%E6%8E%A2%E6%B1%82-Peter-Seibel/dp/4274068471
は生ける伝説達へ聞き取り調査の記録だが、驚くべきことに、彼らにはprintfデバッグを使う者がいるということだ。
Coders at Work on finding and preventing bugs
https://www.benkuhn.net/caw-errors

print文。
プログラミングの神は曰っているではないか。
『汝、誤りあらんと汝の思うところにprintf文を設え、再コンパイル、実行せよ。』
– Joe Armstrong
大抵は、単に値をprintするだけです。
私がプログラムを書いているとき、大量のprint処理が生じます。
そして、私がprint処理をコメントアウトしたり取り除いた時には、それらからprintされていた値は確かなものとなります。
取り除いたprint処理を再挿入することは滅多にありません。
– Ken Thompson
GHCには、様々なものをややバッチ処理的に印字する機能があります。
もしくは、それでも何が悪いのか分からないときが時々あるので、"unsafe printf"を少々バラ撒いて、何が起きているのか印字させます。
– Simon Peyton Jones
このうち、C言語の作者であるKen Thompsonがprintfデバッグを使うのは、驚くには当たらない。
C言語すら存在しない彼の時代には、マトモなデバッガは望むべくもないからだ。
しかしhaskellの主要開発者であるSimon Peyton Jonesと、Erlangの作者Joe Armstrongは違う。
これらは新しいプログラミング言語であり、それなりの各種ツールが利用可能な時代に作られている。
そして、情報不足を理由にそれらの各種ツールを避けるには、彼らはプロフェッショナル過ぎる。
なのに、特にJoe Armstrongはprintfデバッグが第一選択だ。
 
私はbrainfuckJITコンパイラを書いたとき、GNU gdbデバッガを沢山つかった。
GNU CとUNIXシステムコールだけで実行時アセンブラ ( ソフトウェア ) - 一人一党党
コンパイラの場合、生成した機械語命令が正しく動いているか確認する必要がある。
しかしデバッグ対象が機械語命令の粒度になると、printf呼び出しの影響が大きくなりすぎる。
機械語命令一個の動作をみるのに、機械語命令十個程度を挿入するのだから、元のプログラムが1/10しか含まれていない別物をデバッグすることになる。
一方、自作のプログラミング言語処理系上で動くプログラムを書いている現在の私は、gdbデバッガを使わない。
デバッガは自作の言語処理系には対応していないからだ。
自作の言語処理系はC言語で書かれているので、言語処理系自体をデバッグするにはgdbデバッガは有効だろう。
だが、そのデバッグ機能を、もう一段上の言語処理系や仮想マシンインタプリタを越えて使うのは、デバッガ自体をその仮想マシンアーキテクチャに移植するのと等価だ。
移植作業と同等の手間がかかるだろう。
Coders at Workに登場する巨人達は、ほとんどが新しい処理系の作者だ。
彼らに必要なデバッグツールは、既存処理系のものではなく、まだ見ぬ新しい処理系向けでなければならない。
これが、彼らがprintfデバッグを重視する理由だと私は考える。


勿論、大抵のソフトウェア開発では、一見すると、自作の言語処理系をデッチ上げることはないように見える。
だが、抽象化を駆使して、ソースコードの再利用性を高めたりサイズを圧縮するプログラミングスタイルでは、知らないうちに自作言語処理系を作っている可能性が高い。
ARMプロセッサなどいくつかのCPUには除算などの命令がなく、これらのCPU向けの処理系では、除算処理を標準ライブラリ内の関数として実装している。
逆に言えば、あらゆる箇所で使う処理を一つの関数に纏めて抽象化するのは、新しい命令を処理系に追加するのと同等だ。
関数=追加命令が多くなるほど、自作の言語処理系との差は埋まる。
高階関数を使えば遅延評価など制御構造まで抽象化でき、ソースコードレベルではhaskellの真似事に手が届く。
本物のhaskellに対応していないデバッグツールが、そのようなhaskellモドキのコードに対応できるはずがない。
ドメイン固有言語、仮想マシンインタプリタ、新しい言語処理系…、これらは抽象化のうち、明示的に既存言語の枠を踏み越えたものに過ぎない。

つまり、それぞれのデバッグツールには、扱える言語に対応した、扱える抽象化の限界が存在する。
その限界を越えた抽象化を欲するなら、新しい言語を生んだ巨人達と同様にprintfデバッグを使うしかない。