新・日々録 by TRASH BOX@Eel このページをアンテナに追加 RSSフィード

2016-06-27

今までどのくらいプログラミング言語を触ってきたか(3秒で挫折したものものも含む) Ver.8

2016/06/27現在のステータス。id:eel3:20150614:1434290001 から1年経て、こうなっている。

なおCSSHTMLXMLは除外*1

よく使っている

AWK (Gawk)
単純なテキストレコードの処理にはAWKで十分。自作ツールをAWKGawk単体で実装することは皆無なものの、シェルスクリプトMakefileの中にコードを埋め込んで他のコマンドと組み合わせて使う機会は依然として多い。シェル上でワンライナーでデータ処理するときにも重宝している。これはこれで十分AWKらしい使い方だよね?
C++
ちょくちょくお仕事で使うが、本職のC++使いではない。C++11やC++14は非常に便利で、better Cでも使う価値があると思う今日このごろ。C++の標準ライブラリは、C++03の時代からC言語の標準ライブラリよりはるかに充実していたが、C++11でさらに充実しちゃってどうするのよ?*2 ラムダ式とかautoによる型推論とか使えるようになって大変ですよ。低水準の処理を行いつつも便利な機能で実装時間を短縮できるのは便利。だけど機能多すぎ/複雑すぎなところはなんとかならないものか。強力な反面、使い手を選ぶ言語だ。
C言語
お仕事での主力言語。シンプルかつ低水準の世界が垣間見れるところが割と好き。とはいえ最近の他の言語と比較すると、シンプルすぎて安全機構が欠けていたり標準の便利機能が少なかったりするので、入門用の言語としては薦められない。にもかかわらず、プログラミング未経験者向けのC言語の本は毎年出版されている――謎だ。クロスプラットフォームモジュール屋としては、現実解としてC89を採用しているものの、そろそろC99とかC11とか次世代の言語とか主流になってほしいところ。えっ、C++なら今からでも大丈夫だって? 冗談がきついなあ、10年以上前の秘伝のコンパイラを使ってる現場じゃあC++03だって無理無理。
DOSバッチファイル
プログラミング言語に含まれるかどうか不明だが、含めてしまう。ちょっとした自動化や、複数ツールを組み合わせて使うときのラッパーとしてよく使う。コマンドプロンプトはシバン(shebang)に対応していないので、スクリプト言語で書いたツールを起動するラッパーとしても多用している。
make (Makefile)
プログラミング言語に含まれるかどうか不明だが、DSL扱いで……いやGNU Makeはそこそこプログラミング言語的か。GNU Make 4.0はさらにプログラミング言語的だな、特にGNU Guileなところが。先人の遺産をコピペしてガシガシ書き換えるだけだった私も、FizzBuzzを切欠に本格的にGNU Makeを触り始めた。伝統的なmakeやNetBSD Make(pmake)やNMAKEとは随分異なるのね。もう素のmakeは書けないかもしれない :) まあでも私はGuileのコードが書けないので、その分だけはまだまだ素のmakeやpmakeに近いはず――といいつつも、最近書いたNMAKEも独自拡張アリアリで使ってたな。pmakeも独自拡張アリアリになるから、要は素のmakeでは書けないのか。
Objective-C, Objective-C++
時代の流れに逆らえず、iOS向けの試作とかが発生するようになった――あれ、でも主に弄っているのはMacアプリのコードだな。最近流行の言語と比べると良くも悪くも80年代的だが、アプリケーションプログラミング用としてはC言語よりマシだし、C++ほど複雑怪奇*3ではない。まあ、アプリケーション開発向けの充実感はApple謹製のフレームワーク・各種ライブラリのパワーによる面も大きいわけで、その肝心のフレームワークMaciOS以外の開発で使用できないのが悲しいところ*4。あとフレームワーク込みでObjective-Cを勉強するにはMacが必要なので、貧乏人にはちょっとつらい。言語的には、Objective-Cハイブリッドな所は好きだが、Objective-C++はハイブリッドすぎて微妙。せめてbetter CなC++にとどめたObjective-C++にしてほしい。C++のクラスとObjective-Cのクラスを、C++ラムダ式Objective-Cのブロック構文を同時に使うのは地獄だ。便利ではあるんだけど、ねぇ。
Ruby
自作ツール実装にて、AWK代替言語の最有力候補。テキスト処理でも重宝するが、バイナリデータへの変換が絡んでくるとAWKよりもRubyを使った方が効果的だ*5。そろそろirbを電卓代わりに使うスタイルも板に付いてきた気がする。to_s(16)やto_s(2)で基数変換して表示できるところが好き。
sed
プログラミング言語に含まれるかどうか不明だが、DSL扱いで*6。テキスト処理用。シェルスクリプトMakefileにて他のコマンドと組み合わせて使う。というか正規表現でのテキスト置換以外の機能を使った記憶が……あったな、dとiとpと=とブレースによるグループ化ぐらいだが。私のレベルではsedでFizzBuzzを書けないので、sedで難しい処理を記述しないようにしている。
シェルスクリプト (/bin/sh)
プログラミング言語に含まれるかどうか不明だが……いや、私的にはシェルスクリプトは立派なプログラミング言語だ。基本的な用途はバッチファイルと同じくちょっとした自動化や複数コマンドを組み合わせて使うときのラッパーだが、実現できる内容は遥かに多い。言語本体(?)がバッチファイルよりも高機能だし、Unixユーザランドはコマンドが充実している。その意味ではMSYSよりもCygwinで環境構築した方がマシだと思う。CygwinやMSYSでは、主要な処理をシェルスクリプトで記述しておき、bashからはシェルスクリプトを利用し、コマンドプロンプトではラッパーのバッチファイル経由でシェルスクリプトを叩く使い方をしている。ただWindows上では処理速度が妙に遅くなる点が不満だ。

あまり使っていない

bash
最近はデフォルトシェルbashな環境も多いので、自分用のツールぐらいは素の/bin/shではなくbashで書いても大丈夫な気がしてきた。shよりbashの方が遥かに便利だからなあ――PerlRuby等には負けるけど。bashスクリプトを書くときの唯一の欠点は、メジャーバージョンごとの差異や各ディストリでのビルドオプションの違いにより、同じbashという名前でも実は千差万別なところだと思う。PerlRubyのバージョンは気にするけど、これがシェルになると意外とバージョンに無頓着になってしまう。なんでだろう?
C#
かつて、勉強を兼ねてC# 2.0を少し触ろうとするも未完に終わった過去をもつ私。あらためてVisual Studio 2013をインストールして、また少しだけ触ることに。言語仕様的にはC# 5.0の環境だが、使用する機能はC# 4.0相当だろうか。変数型推論ラムダ式LINQデフォルト引数は便利っすね。それと.NET Frameworkの機能数は反則ものだが、所々に微妙に抽象化が行き過ぎたAPIが見られるのは気のせいだろうか? それにしても、クラスが必須ではないC言語C++に慣れてしまった弊害か、アプリケーション・メインエントリすらclass内に定義しなくてはならないC#には、なかなか慣れない。Javaよりはマシなのだが。
D言語 2.x
仕事柄「C/C++の次のシステムプログラミング言語」はそれなりに興味の対象で、Go言語ほどではないが、D言語も気になる存在だ。D言語シンタックスがC・C++に近いだけでなく、コーディングしている時のアプローチ・判断についても、CやC++での流儀がそこそこ通用しやすい気がする。少なくとも、Go言語でコーディングするよりは、文化的背景の違いによるモヤモヤは感じにくい。あと、標準ライブラリを使ってテキストフィルタを書いたところ、エラー処理を1〜2ヶ所のtry - catchにスッキリまとめることができて、ちょっと驚いた。throwされる例外のメッセージ文字列が、ちょうどよい塩梅の内容だったため、メッセージを変更する(いったんcatchして、再throwする)必要がなかった。ちょっと残念なのは、マルチバイト対応だが……。
flex (lex)
プログラミング言語に含まれるかどうか不明だが、DSL扱いで。字句解析用のツールという印象が強かったのだが、よく考えてみたら、flexってsed(1)のよくある使い方と同様に「正規表現でパターンマッチング --> 何らかのアクション」ということをやるためのツールだった。ただ単に、「何らかのアクション」をC言語で書けることと、flex自体ではパターンマッチングをせずに「パターンマッチングするC言語のコード」を生成することが少々風変わりなだけ。grep(1)やsed(1)その他で小ツールを実装して運用しつつ、性能が求められたらflexで専用ツール化するとか――夢が広がるな、いまさらながら。
Go
寡作ながらもいくつか小ツールを書いてみたが、標準ライブラリが充実しているコンパイラ型言語っていいっすね。C言語に比べればC++の標準ライブラリも充実しているが、どちらかといえばプリミティブな機能が中心だ。PythonRubyばりの標準ライブラリを抱えているGoには及ばない。その辺は、やはりCプログラマ(特にCでフィルタデーモンの類を書く層)には受けそうな言語だと思う。並列処理周り(goroutines)とかARM対応とかが気になる。ソフトリアルタイム限定だが「組み込みLinux + Goで書いたデーモン」とかどうだろう? ただメモリを食うらしいという噂がどうなったか気になる――64bit環境では解消されるという話だったようだが、32bit環境でも解消されるようになったのだろうか? 組み込みでは現時点では逆立ちしたって64bit CPUはありえないからなあ、スマホタブレット以外では。
Groovy
JDKがなくてもJava APIを叩くスクリプトを書けるので非常に便利。動的型付け言語っぽくいくもよし、@CompileStaticや@TypeCheckedで型推論するもよし。言語仕様はJavaよりも好みだ。コンソールアプリを書く人としては、オプション引数解析用の機能を標準で持っている点で、GroovyClojureScalaよりもポイントが高い*7。個人的には、IoT時代に「Java VMベース」の言語としてどこに活路を見出すのが、興味深く見守りたいところ。やはりサーバサイドだろうか?
Perl 5
時々、やむをえない事情で触ることがある。だが基本的によく分からない。何というか、あの記号の羅列っぽさに中々慣れないというか、自分は余りに自由度が高すぎる言語は苦手だと気づいたというか。(言語仕様に慣れているなら)半ば使い捨てなテキストフィルタとかをさっと書くには、悪くない言語だと思うのだが。
Python
Perl 5と同様に、やむをえない事情で触ることが多い。Open usp Tukubaiが入っている環境でスクリプトを書くとなると、確実に使える処理系Python 2.xなので、Python 2.xを念頭にコードを書くことになる。しかし日本語を扱うにはPython 3.xの方がよいらしい。うーん、どちらを学べばよいのだろう? Perl 5やRubyと比べると、Pythonではlazyなスタイルが許されず、整然とコードを記述する必要がある。その辺り、Perl 5やRubyとは随分と雰囲気が異なる。Pythonで気になるのは、インデントが必須な言語仕様であるために、シェルスクリプトに埋め込んで使うのが苦痛な点だ。使い方を間違っているのだろうか。
QML
宣伝文句のとおり、QMLはGUIの記述に非常に向いている。それも、単に標準のUI部品(エレメント)を使うだけでなく、少し改造して使うとか、オリジナルのUI部品を作って使うとか、それらを別のアプリケーションに使いまわすとか、そういう時に威力を発揮する。あと、プロパティバインディングやレイアウトのアンカー指定など、画面サイズの変更に追随するUIを作りやすい機能も揃っている。JavaScriptでちょっとした処理も記述できる。とはいえ、やりすぎるとパフォーマンスの罠が……。少なくとも、JavaScriptでゴリゴリコードを書くのはQML的ではない。QMLは宣言的に、シンプルに書くものだ(というか、力技でロジックでゴリ押しすると、色々と罠に嵌る)。個人的には、QDialのようなダイアルのコンポーネントが標準で用意されていたらなあ、と思っていたらQt 5.5で追加された!
Scheme
GaucheWindowsネイティブ環境用バイナリは実験版だが、私が触る分には何の支障もない*8ことに気づいて久しい今日この頃。『Scheme手習い』と『Scheme修行』を購入したので、とりあえずCommon LispではなくGaucheScheme)の勉強をする方向に転換しようか検討*9しているうちに何年たったのやら。Gaucheフィルタ・ライクな小ツールの実装用としても良い感じだ。しかし最も多い利用方法はREPLを電卓代わりにすることだ*10。うーん、作業環境がMac OS XLinuxに移ったなら、大手を振ってGaucheフィルタを書くのだが。
Tcl/Tk
Tclは書き方を忘れた頃にテキスト処理ツールを書いている気がする。Tclは結構独特な言語だ。構文がシェルスクリプトばりに全てコマンドだったり、値が全て文字列だったり、実はリスト構造だったり、意外とTCPソケット通信が得意だったり……。それでも慣れれば結構使いやすい。意外とプロトタイピングに向いている気がする。8.6以降ではオブジェクト指向プログラミングもOKだが、それよりも例外処理用のtry末尾呼び出しの最適化用のtailcallの方が興味深い。しかし、これからメジャーになる可能性は低そうだ。Tkは……小規模なGUIツールをさくっと構築できるところは便利だが、Webアプリ全盛の時代にどれだけ訴求力があるのやら。
Vim script
少し触ってみた分には、exコマンドの拡張(=コマンドの羅列)とは気づかない程度にはプログラミング言語らしいと思う。とはいえ妙なところで嵌ったり微妙に一貫性のない部分があったりするので、その辺りで好き嫌いが別れる気がする。
Windows PowerShell
v5.0も出たというのに、自分も周囲もWindows 7ユーザなのでv2.0で頑張っている。スクリプト言語としてのPowerShellは――またMicrosoftもエライ代物を出してきたものだ。シェルスクリプトなのにオブジェクト指向.NET Frameworkを触れてダイナミックスコープでスクリプトブロック(という名の無名関数)って、無茶しやがって……完全にプログラマ向けじゃないか。あえていえば、コマンドプロンプト上に構築した既存のツールや作業環境との親和性が微妙にイマイチなのが玉に瑕かも(特に文字コードとか)。それにしても、PowerShell 3.0は無理としても、せめて2.0に対応した言語解説本(日本語のもの)が出てくれないだろうか。『Windows PowerShell イン アクション』は良書だが1.0の頃の本なので、他人に薦められないのだ。

最近使ってないが、縁は切れてない

bison (yacc)
プログラミング言語に含まれるかどうか不明だが、DSL扱いで。やっぱり構文解析系統のコードを自作するのは割に合わない――だなんてうそぶきつつ、LALR法とか全く知らないままに、既存のyaccのコードを切り貼りして遊んでいるところ。まだ簡易電卓レベルだが便利さを体感しつつ、さっそくtypo 1文字で痛い目(shift/reduce)に遭った。とりあえず、flexと組み合わせた上でのエラー処理(エラーメッセージの改善)が課題だ。
CASL II
生まれて初めて触れたプログラミング言語その1。何だかんだで、後でCプログラマになってからも低水準での思考ツールとして微妙に役に立っている。まあ考えるための言語であって実用言語ではないけど。仮に実用的な処理系*11があったとしても余りに命令がシンプル過ぎて悶絶するなあ、なんてFizzBuzzしてみて思った。
Java
生まれて初めて触れたプログラミング言語その2。でも、もう忘れてしまった――と思っていたのだが、思い立って昔どこかから拾ってきたツールのコードに手を入れたり、急にAndroid用のサンプルアプリを触ることになったり。うーん、JDKインストールしたり更新したりするのが面倒だ……。とりあえず、構文の見た目は保守的なC++に似ているが中身はC++とは似ても似つかない代物だということは分かった。
Clojure, Scala
JDKがなくてもJava APIを叩くスクリプトを書けるので非常に便利。Scala型推論とか、便利っすね。言語仕様はJavaよりも好みだ。とはいえ、IoT時代にJava VMベースでどこまでメインストリームに居残ることができるのか? ちょっと興味深い。サーバサイドに活路を見出すのだろうか?
JavaScriptクライアントサイド)
組み込み系のCプログラマ世間の流行には逆らえず、クライアントサイドJavaScriptのコード書いてみた。機器のWeb管理コンソールとか、Webアプリとの連携とか。言語仕様的に単体テストしやすい(モックへの差し替えが簡単な)ところが羨ましい。無名関数クロージャも好きだ。
JavaScriptサーバサイド?)
Node.jsやPhantomJSが出てきたこともあり、クライアントサイドJavaScriptサーバサイドJavaScriptの2大潮流とは別に、JavaScriptフィルタ等の小ツールを作る文化が広まらないか少しだけ期待中。いやあ、TCP絡みのダミーサーバもどきをサクッと作るのにNode.jsが地味に便利だなあ、と。
Lua
Wiresharkパケット解析スクリプトを書いてから数年。今度はC言語で書かれたUnixデーモンの設定ファイル用に組み込むことに。これはこれで「職業柄」の範疇、なのだろうか? Windowsのことを考えなければ、自前でライブラリビルドしてアプリに組み込むのは結構簡単だった。
SQL
生まれて初めて触れたプログラミング言語その3ぐらいに位置する。MySQLを入れてコンソール用のモニタからSQL手入力で弄って遊んだことがある。組み込みの人なのでSQLとは無縁だと思っていたが、まさかTransact-SQLをちょっとだけ触ることになるとは。
Visual Basic .NET
いまさらVisual Basic .NETである。しかも2003。古いシステムの改修と移行だから、仕方ない。
XSLT
縁が切れたと思いきや、仕事でXHTMLから特定要素を抜き出す作業に使うことに。XMLからテキストレコードに変換してしまえば、後はUnix流テキストフィルタの世界が待っている。餅は餅屋というもので、定型的なXMLの変換はXSLTで記述すると楽だ。唯一気に入らないのは、xsl:sortでアルファベットの大文字と小文字を区別してソートすることができないこと。ぐぬぬぬ。

これから(また)使うかもしれない

Alloy
形式手法の中では比較的カジュアルに使えそうなので期待中。入門書処理系も入手した。私の場合、先に何か論理型の言語をかじった方がよいのかも。
Common Lisp
2009年に勉強しようと思い立ったものの、未だに進んでいない。階乗とかハノイの塔とかiotaぐらいは書いたが、目標は「ちょっとしたツールを自作する」だ。まだ道は遠い。最近は時々CLISPを簡易電卓代わりにしている。
Coq
ソフトウェアの基礎が気になるので、処理系だけ入手。
Emacs Lisp
.emacsコピペ」限定で。Common LispSchemeを触ったためか、何となく内容を追えるようになってきた気がしていたが、勘違いだった。
F#
OCamlは「Windows上で日本語を扱う」という視点では処理系がちょっと微妙なので、いっそのことF#に乗り換えようかと……。『実践F#』が積読状態になっている。
Forth
pForthをMinGWビルドしたので処理系は手元にある。スタック指向の言語はいつか勉強したい。
Io
プロトタイプベースである点を除けば、何となくSmalltalk的であるような――公式ドキュメントらしきIo Programming Guideでも影響を受けた言語として真っ先にSmalltalkが挙げられているから、あながち思い違いでもないだろう。
LOGO
そういえばLOGOを触ったことがない。とりあえずUCBLogo(Berkeley Logo)だろうか? Windows上でUCBLogoばりにGUI無しで動作する処理系はないだろうか?
Object REXX
思うところがあって処理系IBM謹製のドキュメントを入手したものの、そこから先の進展は無いまま。ReginaでClassic REXXっぽい感じで触っているからなあ。
OCaml
Common Lispを勉強するはずが、いつの間にか触っていた言語。一応、階乗ぐらいは書いた。時間が取れたらもうちょっとしっかりと勉強したいが、面倒なのでF#に移行しようか検討中。
Oz
ふと思い立ってUbuntuにMozartを入れた。『Scheme手習い』の次はCTMCP片手にOzで勉強かなあ。道は遠いな……。
PostScript
これかForthか、どちらに手を出すべきか? 悩ましい。
Processing
入門書処理系も入手して、あとは弄る時間をつくるだけ。
Prolog
少し前に触っていた言語。『7つの言語、7つの世界』の地図の色分けプログラムには衝撃を受けた。何というか「正しい問い」を見つけられるか否かが肝なのか。この辺は、根底の部分でAlloyに通じる何かがあるように思う。ひとまず、Prologで論理プログラミング宣言的なスタイルに慣れておけば、形式手法にて「論理で宣言的に」記述するときに戸惑いが減るのではないかと期待している。
Rust
仕事柄「C/C++の次のシステムプログラミング言語」はそれなりに興味の対象で、Go言語やD言語ほどではないが、Rustも……まあ、気にならなくはない。ちなみに、これら3言語と同列にObjective-CSwiftが挙げられることもあるようだが、個人的見解としては、システムプログラミング言語としてのこの2言語には全く期待していない。あれは、Appleというしがらみからは逃れられないでしょうな。
Smalltalk (Squeak, Pharo)
Smalltalkは有名な古典的プログラミング言語だというのに、触ったことがない。ということでSqueakとPharoの処理系のみ準備完了。うーん、「環境」付きなのが気になる――言語を弄くる基準が「コンソール上でテキストフィルタ」という変な人種な私だからなあ。
Smalltalk (GNU Smalltalk)
個人の思想信条による理由よりSqueakとPharoにわだかまりを感じてしまう変人なので、邪道だと思いつつもコンソールでテキスト処理もOKなGNU Smalltalkも用意してみた。これで言語としてのSmalltalkの勉強に集中できる……か?
Swift
Appleの新言語については未だ未知の代物なのだが、その割には見覚えのあるシンタックスのような……。Objective-Cを駆逐して、D言語・Go言語・Rustと並ぶ4大次世代システムプログラミング言語となるか、はたまたMac/iOSアプリ専用(システムプログラミングなんて関係ないね!)となるか……まあ、後者だろうなあ。Objective-Cと併存可能ということは、処理系LLVMのフロントエンドとして実装しているのだろうか?
VBA (Visual Basic for Applications)
今までVBAから逃げ回っていたのだが、ついに使うことになりそうな予感。たぶん、Access VBA 8割にExcel VBA 2割ぐらい。

今は全く使っていない

Active Basic
VBScripを触りだした影響で、時々思い出しては弄くっていた。ほんの少しだけ触って放置して、すっかり忘れてからまた触る――これを繰り返していた気がする。なので毎度初めて触るのと同じ状態だった。String型をバシバシ使用 :)
bc
その昔、Windows標準の電卓アプリの代わりに使おうとして色々あって挫折した。今はirbclisp/goshで計算しているからなあ。
COBOL
FizzBuzzするためだけにOpenCOBOL 1.0をWindows上に用意して触ってみた。なんというか、COBOLの名前と生まれた時代が示すように基幹業務(というかお金や帳簿が絡んでくるところ)向けの言語だよなあ、といった感じ。COBOL 2002のフリーフォーマットを採用するだけでも使い勝手が変わる気がしたが、世の中にはまだ広まらないのだろうか。
CoffeeScript
仕事で使う予定はない。RubyPythonその他の影響を受けているだけあり、その手のスクリプト言語っぽい感じでコードを書けるので、慣れれば素のJavaScriptで直接コーディングするよりは楽だ。しかし標準ライブラリ回りや処理系絡みの機能やサードパーティライブラリなど、結局はJavaScriptを知らないとCoffeeScriptでコードを書けないと思う。それに生成されたJavaScriptのコードを見て「うわぁ、これあまり効率的でないなあ」と感じる時もあって、高速化が必要な部分では生成されるコードを気にしながら記述したりCoffeeScriptを諦めてJavaScriptで書くことになるので、やはりJavaScriptを知らないとマズイ。とはいえ便利なのは確かだ。CoffeeScriptのコードは即Node.jsで実行できるので、その辺りから「CoffeeScriptでテキストフィルタ」的な文化が生まれると面白いかも。気になるのはECMAScript 6の存在で、今までCoffeeScript独自の機能だった部分の多くがES6で取り込まれるので、今後ES6対応が進むにつれてCoffeeScriptの立場がどうなっていくのか、少々興味深い。
Fortran
Fortran 90やFortran 95あたりは結構近代的な言語だと思う。用途次第ではC言語よりもFortranの方が遥かにマシな選択だろう。配列がらみの処理はFortranの方が得意だし、言語機能としてのモジュール化の方法はC言語には存在しない。可変長な文字列の扱いに微妙な制限がある点はマイナスな気もするが、まあ基本的に数値計算プログラム用の言語だからなあ。
GDB (GNU Debugger)
……いやGDBデバッガとして使っているが、GDBスクリプトを書く機会は(FizzBuzz以外に)ない。勉強不足なだけかもしれない。
HSP (Hot Soup Processor)
FizzBuzzで楽しんでみたが、何というか他言語経験者には受けが悪そうな命令体系だと思う。もっとも初心者がプログラミングという行為に深入りせずにWindows用のGUIな何かを作る分には、あの命令体系でも十分な気がしないでもない。ところで元々は「HSPで職業プログラマ的な良いコードを書くと、どんな感じになるか?」というネタを思いついて処理系を用意したのだけど、そちらは全く進展がないまま。
JScript on WSH
他人が使うテキスト処理ツールの実装に使って以来、時々触ってきた。Windows用の配布可能な小ツールを実装する時の定番言語だった。でもそろそろ潮時だろう。HTAと組み合わせてクライアントサイドJavaScriptなノリで簡易なGUIツールを実装できる点も、PowerShell + WPF + XAML代替できそうだ。他のメリットは「JavaScriptECMAScript)でフィルタを書ける」だったが、WSHのなかなか目的にたどり着けないオブジェクト階層にイライラするよりも、Node.jsやPhantomJSを使ったほうが精神衛生的にマシだ。
m4
その昔テキスト処理用に触ろうとして、Windows用のどの処理系も日本語の置換に何かしらの問題を抱えていたので泣く泣く諦めた。思うところがあって改めて少し触ってみたが――なるほど、確かに中毒性のある言語*12だ。
REXX
Open Object REXX処理系を入手したのに、何故かReginaを入れてClassic REXXっぽい方向に走っていた。何というか、COMコンポーネント.NET Frameworkと無関係でいられるのなら、バッチファイルの代替としてはREXXあたりがほどよい塩梅だと感じる。しかし最近流行の言語とは随分と勝手が違うし、日本語の情報も少ない。メインフレーム以外の世界で流行る可能性は少ないだろう。
T4 Text Template
「へえ、こんなものがVisual Studioに入っていたのか。機能多すぎで色々と便利なツールを見逃しているんだな、やっぱり」と思いつつ触ってみた。テンプレート変換の用途ではピカ一だと思う。ただ処理系を手に入れる方法が「Visual Studioインストールする」or「MonoDevelopインストールする」なので、何となく「単体で手軽に使えるツール」ではないというイメージが……。まあC#VBで処理を記述するので、それらの処理系が必要だという面での制約なのだろう。
VBScript on WSH
JScriptほどではないが「Windows上で他人も使えるツールを書くためのLL」扱いしていた言語。Windows Server管理の関係で触っていた。というかWebで入手可能なWSHのサンプルの大半がVBScriptで書かれていたり、ADSI関連のコレクションをJScriptで舐めれなかったりして、結局は必要に駆られて使用することに。明快に記述できる文法は評価に値するが、スクリプト言語としては少々冗長だ。配列は自動拡張しないし、組み込み関数はプリミティブ気味だし、冗長気味な文法との合わせ技でコードがさらに冗長になっていく……。文法や言語仕様の詳細なドキュメントが見つからないのだが、どこにあるのだろうか?*13
秀丸マクロ
7年ほど秀丸エディタを使っていたが、マクロを書く機会はなかった。一念発起してFizzBuzzしてみて感じたのは、最近の便利な言語に慣れた身としては色々とモヤモヤ感がある言語仕様だということ(歴史的経緯的に仕方ないのだが)。とはいえちょっとした拡張ツール的なものを手軽に作れそうではあった。

*1:というか人工言語ではあるけど「プログラミング言語」という括りからは外れると思う。

*2:とはいえ、C++標準ライブラリの充実度をJavaC#.NET Framework含む)のそれと比較してはいけません。

*3:少なくともC++の言語仕様は私の手には余る。自分が把握している範囲の機能でコードを書くのは好きなのだけど。

*4GNUStepの互換性は高いけど高くないというか、マンパワーその他の要因で実装が追いついていないというか。

*5:とはいえ、ついついC++を使ってしまうのだよなあ。

*6:これでもsedチューリング完全な言語だと証明されているらしい。

*7ClojureScalaに関しては、同様の機能を私が見逃している可能性も高いのだが。

*8:支障がある部分を触るほど深入りするには、あと20年ぐらい掛かるのではないか?

*9Schemeの勉強というよりも、再帰の勉強なのか?

*10:現状はirbclispかgoshの3択だ。

*11:――といってもシミュレータだけど。

*12m4マクロプロセッサなのでプログラミング言語ではないはずだけど……。

*13MSDNの資料では物足りない。もうちょっと掘り下げた内容のものが欲しい。

2016-06-17

書籍購入:『プログラミング言語Go』

まごうことなく、プログラミング経験者向けのGo言語入門書の良書。

本書の雰囲気は、『プログラミング言語C 第2版 ANSI規格準拠*1)や『UNIXプログラミング環境 (海外ブックス)』に通じるものがある。あと『ソフトウェア作法』も少々。カーニハンの本のスタイルが肌にあう人なら、本書は最適の入門書だろう。

まだ「第1章 チュートリアル」を読んでいるところだが、コンパクトだけど実用面を視野にいれたサンプルコードが頻繁に出てくる。これ、勉強がてら実用ツールを作ろうとした時に便利だ。

プログラミング言語入門書にありがちなのが、言語仕様・ライブラリ機能の挙動を単に示すだけのサンプルコードだ。それはそれで十分興味深い内容ではあるのだが、「まだ言語仕様を深堀りする段階じゃない! Go言語の流儀のポイントを押さえつつ、動くコードを書きたいのだ!」という段階では、残念ながら細かすぎてあまり役に立たない。

その点、本書はバランスが良くて、分かりやすい本文と、実用面を視野に入れたサンプルコードが、うまい具合に合致している。いい仕事してますな。

*1:ただし翻訳の品質を除く。『プログラミング言語C 第2版』は翻訳の品質が、ちょっと……。それさえなければ、「Unix系OSでのシステム・プログラミングを視野に入れた、プログラミング経験者向けの入門書」としては、セキュア・コーディングへの配慮に欠ける点を除けば、悪くない部類に入ると思う。

2016-05-13

書籍購入:『Unix考古学』

私のように待ち望んでいた(そしていつの間にか忘れてしまっていた)人は意外といるはず。

Unix考古学 Truth of the Legend

Unix考古学 Truth of the Legend

ただし私は「UNIX USER」誌の読者ではなく*1、連載の数年後に『ホゲゆに』→SIMH→「UNIX USER」誌のバックナンバー、という順序で辿りついた口だ。同じような人はいるのだろうか?

本書は、UNIX誕生前夜(1960年代半ば)からバークレーでのBSD開発終了(1990年代前半)までの、UnixUNIXAT&TコミュニティBSDARPANET)の歴史について、分散している様々な資料をもとに再構築して解説した本だ。

様々な資料――当時の論文や、当事者たちが後日に書籍やインタビューで語った話などを探し出して丹念に洗い出し、結びつけと分析を行い、推測する。文字通り、発掘作業を伴わない、文献調査による考古学だといえるだろう。

しかもこれ、元ネタは10年以上前の「UNIX USER」誌での連載をまとめたものなのだが、当時の連載内容から色々と変更されているだけでなく、3分の1ぐらい新たに書き下ろされている。この10年分の最新の研究成果を反映したかのようだ。

連載時にはSIMHを使ったUNIX v7の実習があったが、本書からは外されている。サポートサイト(Github)に当時の校正前原稿その他がアップされており、それらを参照するかたちになる。

書籍購入:『Winnyの技術』

P2Pネタの技術調査にあたり「そういえばWinnyの解説本が出ていたな」と思い当たったので購入。

Winnyの技術

Winnyの技術

直近で知りたかったのは、「通信相手との接続確立の方法」と「TCPベースでのNAT越え」の実例だったのだが、Winnyではその辺を巧妙に回避していたのだった。

うーん、「インターネットに直接接続しているコンピュータや、NAT越しにポートフォワーディングしているコンピュータを含む、多数のコンピュータで構成されたメッシュネットワーク」を前提としているのか。ポートフォワーディングせずにNAT越しにインターネットと接続しているコンピュータ同士が直接P2Pすることはなくて、必ずメッシュネットワーク上の「直接接続しているコンピュータ」ないし「ポートフォワーディングしているコンピュータ」を経由するようにしている。

この方式って、「常時接続・常時稼動」のコンピュータを前提としているようだ。

モバイル機器だとちょっと難しくて、というのも、いくらバッテリーが進化しようとも、バッテリー容量の問題は付いて回るだろうから、常時接続・常時稼動は厳しい。契約形態によっては、通信料金の問題も発生するだろうし。

まあ、ともかく、興味深く面白い本だ。

*1:連載時はコの業界にはいなかったし、プログラミングとも無縁だった。

2016-05-08

ブライアン・カーニハンは写経したか?

ちょっと前の記事だけと、少し気になったので書いておく。

要約するのが面倒なので、内容については上記記事を参照してもらうとして……以下、上記記事を読んでいるという前提で、色々と省きながら書く。

上記記事では、プログラミングの上達にて効果的な手段として「写経」を挙げている。といっても、単なる写経ではなく:

そして筆者自身も、新しいことを勉強するのに、サンプルプログラムをとってきて改造するのをやめて、サンプルプログラムを一度印刷し、一言一句、上から下まで写経すると、驚くほどコードへの理解が深まることに気づきました。

なぜかというと、写経というのは、「なにも考えなくて良い」と言われるものの、人間ですから考えてしまいます。しかも、一言一句、記号のひとつひとつまで、意味を噛み締めながら入力していきます。

すると、サラッとサンプルプログラムを眺めているだけでは気づかなかった細かい法則や作法、サンプルプログラムを制作した製作者の意図などといったものがどことなくつかめてくるようになるのです。

もちろん、最初は意味がわからないなあ、と思って写しても構いません。

それでも、打ち込んでいくと、打ち間違いをしても、「あれ、これは少し変だぞ」と気づくようになります。

「さっきまではこの順番で丸括弧が来ていたのに、ここだけカギ括弧になっていて変だぞ」などということに、本能的に気づくのです。

https://wirelesswire.jp/2016/04/52633/

――正直なところ、個人的見解だが、これを「写経」といってよいものか疑問に思うところだが……。

ここで思い出したのが、K&RのKであるブライアン・カーニハンが『言語設計者たちが考えること (THEORY/IN/PRACTICE)』にて自身のプログラミング言語学習法について述べていたくだり(以下、同書初版第1刷のP.126より):

■言語を新たに学ぶ際には、どのようにするのですか?

Brian:言語を新たに学ぶには、行いたいことと近い内容の例を探し出し、それを使って学習していくのが最も近道になると考えています。例をコピーし、ニーズに合うよう変更した後、それを動作させることで知識を広げていくのです。このようにしてさまざまな言語を触っていると、頭の中が混乱し、ある言語から他の言語に頭を切り替える際に時間がかかるようになります。思い出そうとしている言語が大昔に学んだC言語と似ていない場合、特に時間がかかります。このため、優れたコンパイラが誤った構造や怪しげな構造の警告を出してくれるのは、学習する上でありがたいものとなります。こういった点でC++Javaといった強い型システムを持つ言語も学習に役立ちますし、標準への厳格な準拠を強制するオプションも良いものと言えます。

さて、先のブログ記事での学習法(仮に「写経」式学習法とでも呼ぼう)とカーニハンの学習法(こちらはカーニハン式学習法と呼ぶことにする)は、対立しているのだろうか? 私には、若干のスタンスの違いはあれど、どちらも全く同じことをやっているようにしか思えない(効率の差は別として……)。

そもそも「ソースコードを読み、内容を理解する」とは、どういうことだろうか?

プログラムは2つの姿をもつ。それは「静的構造」と「動的構造」だ。静的構造はソースコードであり、ソースコードによって表現された「プログラムの構造」だ。一方の動的構造は、プログラム実行時の実際の計算の進行(≒実行時の挙動、振る舞い)だ。

動的構造は、人間の目には見えない。プログラムの構造によっては、例えばデバッガを使ってステップ実行することで、動的構造に非常に近い環境を再現することが可能だ*1。だが現実のソフトウェアでは、例えばマルチスレッド・マルチプロセス割り込みなどに起因する「タイミングの問題」など、その方法では再現できない動的構造がある。何よりもソフトウェアの大きさが、動的構造の全て(≒全ての分岐パス)を理解することを許さない。

プログラムを理解する」とは、最終的には「プログラムの動的構造を高い精度で推測できるようになる」ということだ。

つまり、「ソースコードを読み、内容を理解する」とは、「ソースコードという静的構造を理解した上で、静的構造から動的構造を高い精度で推測できるようになる」ということを意味する。

(余談だが、「ヒトの頭では『大規模プログラム』の動的構造を全て理解するのは無理だから、静的構造と動的構造が極力一致するようにして、静的構造(≒ソースコード)から動的構造(≒プログラムの動作)が分かるようにしようぜ」というのが、構造化プログラミングにおけるGOTO文をめぐる話の核心だ*2

静的構造から動的構造を推測するためには、まず、静的構造を正しく理解できなくてはならない。つまり、ソースコードという記号の羅列を、意味のあるものとして解釈できなくてはならない。その上で、静的構造が動的構造にどのようにマッピングされるのか、判断できるようにならなくてはならない。

このように書くと、「ソースコードを読み、内容を理解する」という作業は、あたかも「まず静的構造を理解し、次に動的構造を推測する」という風にキレイに順序化されるものであるかのように見える。

しかし実際のところ、「静的構造の理解」と「動的構造の推測」は、混在した作業だ。静的構造の理解は、使用している言語・ライブラリフレームワークについての土地勘がないと、途端に難易度が跳ね上がる。また、静的構造だけ*3から動的構造を推測することも難しい。現実には、ある初見のプログラムを理解しようとする時、ソースコードを読む作業だけでなく、「ソースコードを読むための技術」で述べられているように動的解析*4を併用することが多い。そして興味深いことに、動的解析の結果を通じて、言語・ライブラリフレームワークリファレンスからはいまいち分からなかった挙動が判明し、静的構造の理解が曖昧だった部分が正しく理解できるようになることがある。

私見だが、「写経」式学習法は「静的構造の理解」先行型のスタイルだ。一方のカーニハン式学習法は、動的解析をやや優先するスタイルだ。スタイルの違いはあるが、しかしどちらも「静的構造の理解」と動的解析を併用している。「写経」式学習法では、ディープラーニングの技術セミナーでの例のように、「写経プログラム実行、ソースコード解説」のような順序で進めている。また、カーニハンが「静的構造の理解」を決して疎かにしてはいないことは、先のインタビューの後半にてコンパイラの警告や強い型システムについて言及していることから推測できる。

個人的に、両者のスタンスの違いがどこから生じているのか、なんとも妄想をかき立てられるものだ。

というのも、カーニハン式学習法のようなアプローチは、例えばかつてAT&Tベル研究所で採用されていた形跡があるのだ。以下、『UNIXカーネルの設計』の序文より:

本書の内容と構成は1983年から1984年にかけてAT&Tベル研究所での教育コース用に準備した資料から生まれてきた。コースはソースプログラムを読むことを目的としていたのだが、ここで、ひとたびシステムの概念とアルゴリズムが理解されているとプログラムの理解は簡単になるという事実を痛感した。UNIXの簡潔さと美しさを少しでも反映するように、アルゴリズムの記述はできるだけ簡潔にした。したがって、本書はシステムの内容の各行を解説したものではなく、それぞれのアルゴリズムの全体的な流れを説明したものである。

UNIXカーネルの設計』はカーネル本なので、背景というか前提条件は大分違うのだが、しかしUNIXカーネルソースコードを読むにあたり、動的解析の代わりに「大まかな全体の流れ」の解説を優先させた上で、その後に「ソースコードを読んで理解する」の段階に進んでいる。(公正な比較ではないことを承知した上で書くが)「写経」式学習法とは逆のアプローチだ。

私は、両者のスタンスの違いは、歴史的経緯によるものが大きいと考えている。

  1. 「便利で手軽で高品質なデバッガ」の普及による、実装スタイルの変化
  2. 「単純で十分に理解されている部品を組み合わせた」時代の終焉
  3. より安全な高水準言語の普及
  4. 宣言的な記述(抽象的な記述)の増加

1番目は、簡単にデバッガを使える環境が整ったことや、そのような環境しか知らないプログラマが増加したことで、実装時に「静的構造の美しさ」とでもいう部分に注力する割合が変化したのではないか、その結果として静的構造を理解する能力にレベル差が生じているのではないか、という仮説だ。現在のアプリケーションソフトウェア開発では、ソースレベル・デバッガの存在は当たり前であるし、大抵は統合開発環境に組み込まれている。デバッガの品質は高く、デバッガ再現する動的構造と実際の動的構造の差異は非常に小さい。このような環境では、デバッグ実行で動作を検証することが非常に容易だ。だから、自身が構築した静的構造に少々微妙な点があったとしても、デバッグ実行で確かめてしまえばよい。しかしデバッグ環境が整っていない場合、プログラムに問題が起きた際に即座に頼れるものはソースコードだけだ。だから、実装の際には静的構造の妥当性や美しさといった要素――すなわち「ソースコードをキレイに記述すること」に注力することで、予めバグを作りこまないように予防すると同時に、後で不具合報告が届いた際にソースコードのみから「静的構造の理解」と「動的構造の推測」を容易に行えるように準備するようになる。これら両者の違いが、プログラムの静的構造に対する姿勢(≒どの程度重視するか?)に影響を与えているのではないか?

2番目は「本の虫: MITがSICPを教えなくなった理由」で述べられている内容と同じだ。かつては、シンプルな言語仕様とスリムな標準ライブラリを用いていた。そのため、開発に用いる言語とライブラリは自分自身の手足も同然だった。この時代を経ている人は、ライブラリをつっつき回す時代になってからプログラミングを学び始めた人よりも、言語・ライブラリフレームワーク・その他ツールを体に覚えさせようとする傾向にあるのではないか、その結果知らず知らずのうちに静的構造を注視するようになっているのではないか?

3番目は、2番目とも関係している。例えば私はC言語が主戦場な人なのだが、C言語使いにとって「プログラムが動作し、意図した結果が得られる」という事実は気休めにもならない。Cプログラマは「それは本当に正しいプログラムか? 偶然、それっぽくいい感じに動いているだけではないか?」と常に疑いを抱く――たとえコードの見た目が正しそうで、問題なく動作しているステートメントでも、警告オプションを高くしてコンパイルした時に警告されないか確かめるし、警告が出なかった場合でも「言語仕様上の問題はないか? 単にこのコンパイラで警告が出なかっただけではないか?」と心配するものだ(だから言語仕様上の微妙な問題に詳しくなるし、コンパイラの「標準への厳格な準拠を強制するオプション」の有無を気にする)。それほど注意深くないと、自身の足を撃ちぬいてしまう。そういった、モダンな言語よりも危険で落とし穴のある言語の経験が長ければ長いほど、ソースコードを注意深く観察する癖が付いているのではないか? 安全な言語を使う機会が増えたことで、観察力が少し落ちているのではないか?

そして4番目。言語・ライブラリフレームワークの高水準化が進み、以前よりも宣言的・抽象的な記述がなされたコードが増えている。実のところ、宣言的な記述から実際の挙動や動作を理解することは難しい(≒理解に必要な知識が多いので、理解できるようになるまでのハードルが上がっている)。非常に乱暴な言い方だが、Java入門文書の当初にて「public static void main(String[] args)」を「呪文」として扱うことに似たスタンスで見慣れぬシンタックスに相対して、「詳細は分からないが、とりあえず目的とした結果を得ている」という水準で済ます癖が付いている人が増えているのではないか? 1〜3番目とハードウェアリソースの向上により、それでなんとかなってしまうケースが増えているのではないか?

言語やライブラリが「手足も同然」から「ままならぬ代物」と化し、コードの記述がより抽象化された昨今では、「静的構造の理解」の難易度が上昇している。かつての、動的解析をやや優先するスタイルにおける「静的構造の理解」への取り組みは、実は徒手空拳も同然であり、今となってはなにかしらの武器が必要なのかもしれない。

また、かつては静的構造を注視し、ソースコードを注意深く観察するタイプだった人でも、1〜4に挙げた環境の変化より、静的構造に取り組む姿勢が軟化しつつある可能性はあるだろう。

そう考えると、「写経」式学習法のように「静的構造の理解」を先行させるスタイルでのプログラミング学習は、現状に合致したものなのだろうか。

*1:実のところデバッガは、プログラムの動的構造を巧妙に再現するツールにすぎない(デバッグ実行中の動的構造は、プログラム本来の動的構造ではない)。この辺は、古い本だが『デバッガの理論と実装 (ASCII SOFTWARE SCIENCE Language)』を参照。

*2:さらに余談だが、「静的構造も、大きすぎると理解できなくなるから、そこんとこ工夫でカバーしようぜ」というが、構造化プログラミングにおける分割統治とトップダウン手法の話だ。プログラミング言語の上に抽象層を構築することで、プログラムの各層の大きさを「ヒトの頭で理解できる大きさ」に抑えられるようにすることと、うまく抽象化することで「本当に必要になるまで上下階層のコードを読まなくても済む」という状態にすることの重要性を説いている。

*3:強調に注意。

*4:例えばデバッガ上で動かしてみるとか、printf(3)(≒トレースログ)を仕込んで動かすとか。

2016-04-25

誰も正しいコメントの書き方を知らない

「コメントの書き方」にもある種の理論・思想があり、理論・思想にもとづく「型」があると思っているのだが、「『コメントの書き方』についてそこまで突っ込んだ理解をしている人は驚くほど少ない」と考えておいたほうが無難だろう。

Code Kataは既出だが、Comment Kataは無い。しかし「型」はある。見落とされているだけだ。

――と、下記エントリとその周辺を見ていて思った。

それはおそらく、コードは動作するが、コメントは動作しないからだろう。コードは主役であり、コメントはパセリだ。ともなれば、我々はコードの品質向上については気にかけるが(なんたって主役なんだから!)、コメントについてはなおざりになりがちであるし、時にはおざなりにしてしまうこともある。

なおざりになりがちだからこそ、新人は先輩/教育担当から「コメントを書け!」としか言われない。で、書き方に悩んだ挙句、ソースコードの右端にアセンブラのごとくステートメントごとに細かくコメントを書いて提出すると、そこで初めて「ここは○○にして、そこは××で……」と個々の案件について具体的な注文が返ってくる。だが往々にして「なぜ○○にするべきか?」という理由は明かされないし、その背後にあるだろう理論・思想はもっと見えてこない。なぜならば、「コメントについて明確な理論・思想をもち、それらにもとづく「型」を明示できる」という水準に達している人が非常に少ないからだ。明確な体系を持たないから、経験と勘にもとづいて個別に指摘するしかできないのだ。明確な体系があるならば、それを提示すれば済むし、体系にもとづく「型」があるのならば、まずは「型」に沿ったテンプレートを使わせるところから訓練を始めてもよい話なのだ。非効率な話だと、常々思う。

(「『リーダブルコード』でも読め」と手渡されるほうが、よっぽど親切だ)

また例えば、手元の和書にて、コメントの書き方について「型」といえるほど偏執的に書かれているのは『CODE COMPLETE 第2版 下 完全なプログラミングを目指して』の第32章「読めばわかるコード」だけだ。1章まるごと、50ページほどコメントの書き方について具体例付きで論じている。

他に『プログラミング作法』『Code Craft ~エクセレントなコードを書くための実践的技法~』『リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)』を持っているし、『プログラム書法 第2版』も読んだことがある。どれも良い本ではあるが、こと「コメントの書き方」に限定するなら、どれも『CODE COMPLETE 第2版 下』には一歩及ばない。『リーダブルコード』や『Code Craft』はコメントの良し悪しに関する理論・思想についてしっかり記述されていて、結構いい線いっているのだが(さらに言えば『リーダブルコード』は安価なので薦めやすいのだが)、しかしまだ少し簡潔すぎる。「型」といえるほど具体的な記述は少ない。

和書を見渡しても、これらの本ぐらいだろうか? 「コメントの書き方」に限っていえば、体系的かつ具体的な記述がなされた文書は少ない。

情報が少なく、また優先順位が一段低いとなれば、良いコメントの書き方の技術に詳しい人は少なくなる。ほぼ確実に、「良いコードの書き方の技術に詳しい人」よりも少ないだろう。

そもそも「コメントが無くても読めるようなプログラムを書け」という格言の意味を、どれだけの人が正しく理解できているだろうか?

「コメントが無くても読めるようなプログラム」は「コメントの無いソースファイル」を意味しない。「(ソースファイルにコメントを書かずに済むように、ありとあらゆる合法的かつ妥当な手法を駆使した結果)(そのコードを共有する文化圏の基準で)最小限のコメントのみが、メンテナンスしやすいスタイルで記述されているソースファイル」のことだ。

コメントの量を減らすには、まず「コメントすべきでないもの」を取り除き、次に「コメントの代替となる何か」に置き換えることになる。この結果、ソースファイルには「コメントすべきもので、かつ代替となるものが存在しないもの」のみがコメントとして残ることとなる。

では「コメントすべきでないもの」から見ていこう。

コメント以前に、ソースファイルはas is、つまり「現在の姿」を映し出すものでなくてはならない。ソースファイルの構成要素はソースコードとコメントだ。コメントも「現在の姿」を映し出すものでなくてはならない。

つまり、ソースファイルの内容とは無関係のコメントは書いてはならない。先に挙げた記事でいうなら、「その場しのぎの処理」は(文章表現は別として)セーフだろうが*1「仕方がない」はアウトだ。それはアンタの感想にすぎないだろう。

また、変更履歴のような「過去の姿」に関するコメント、特に「// ○○対応のため追加(2016-04-25:eel3)」のようなコメントも書いてはならない。この手のコメントは、驚くほど早く劣化する。下手すると次のコミットにて邪魔な過去の遺物と化してしまうだろう。そういう情報はソースファイル中に書くのではなく、バージョン管理システムコミット・ログや、プロジェクト管理ツールのチケットへのコメントなど、履歴の関する情報を管理するための適切なツールを用いて記録するべきだ。個人的には、分散バージョン管理システム(というかGit)の普及で、ようやく撲滅できる環境が整ってきたと考えている*2

「コメントすべきでないもの」は、実はこれだけだ。世の中で「コメントすべきでない」とされているもの*3の大半は、実際には「『コメントの代替となる何か』に置き換える」ことが可能なコメントに該当する。

鍵となるのはDRYDon't Repeat Yourself)だ。コメントを書くことで、ソースファイル中の特定のソースコードとの間に「記述の二重化」が発生するのなら、そのコメントを削除できないか、一度は検討するべきだろう。

なぜならば、「記述の二重化」が発生することによって、ソースコードを変更する際に、対となっているコメントも変更して同期させる必要性が生じるからだ。2ヶ所メンテするよりも1ヶ所の方が楽だし、もう片一方を変更し忘れることもない。

この時、コメントの代替――すなわち対となるソースコードの表現、特にステートメントの表現を見直すことは、鉄板ネタだ。世の中の大概の「コメントを減らすべき」という主張は、ステートメントの表現の見直しを指している。

というのも、ソースコードの構成要素をあえて「式とステートメント」と「データ構造」に分けるとすると、「式とステートメント」についての変更は頻繁に発生する。なので、もしステートメントAに密着したコメントaが存在するなら、Aを変更する際にaも変更しなくてはならない。そんな作業を何度も繰り返すのは無駄以外の何者でもないのだ。

かくして、ありとあらゆる手段でステートメントの可読性を向上させることで、ステートメントに密着したコメントを取り除こうということになる。適切な名前・適切な順序・シンプルなアルゴリズムの採用……典型的な「良いコードの書き方の技術」だ。

コメントの書き方でよく言われる「HowよりWhat、WhatよりWhy」的なアドバイス*4でいえば、ステートメントに密着したコメントは「How」に該当する。ステートメントの可読性を向上させることで解読補助用のコメントが減るため、「How」のコメントも減る。

ステートメントについての「What」は、基本的に要約コメントだ。関連するステートメントの塊について、何をしているのか、ドメインに着目して書く。要約コメントの典型例が「ファイルのヘッダ・コメント」と「関数メソッドのヘッダ・コメント」で、要約コメントの中では最も多く書かれている。関数メソッドが長くなった場合、中身のフェースごとに1〜2行ずつ要約コメントを足すことがあるが、稀な部類に入る。何を行っているかぱっと見では分かりにくいステートメントの塊について、ステートメントの可読性向上だけでは対応できない場合に、要約コメントを足すこともある。

関数メソッドのヘッダ・コメントには濃淡がある。古きよきJavaのgetterやsetterのようなルーチンは、名前から役割が明白であるし、中身もシンプル極まりないので、ヘッダ・コメントは書かれない。モジュール/クラス内部でのみ用いられるルーチンでは、概要を述べた1行コメントで済ますことも多い。外部に公開されるルーチンには、もう少し詳しい情報を提示するかもしれない。不特定多数の人が利用するライブラリともなると、さらに厳しくなる。

特に外部公開インタフェースに関しては、「コメントではなくテストコードやサンプルコードで」という主張もあるが、それでは不十分であるケースもある点は押さえておくべきだろう。例えばシステムコール由来のエラーが発生した際のルーチンの振る舞いについて、テストコードやサンプルコードにて、どのように読者に伝えればよいのだろうか?

思い起こしてほしい。我々はUnixシステムコールを叩くとき、サンプルコードだけ読むだろうか? 否。manサンプルコードの双方を読むはずだ。Unixmanは、不特定多数が利用するライブラリのドキュメントの典型例だといえる(その良し悪しは別として)。

外部公開インタフェースの解説について、テストコードやサンプルコードで済ますことが可能なのは、そのコードの作者と利用者の双方に「暗黙の共通認識」が存在するケースであることが多い。

これは外部公開インタフェースに限らず、例えばモジュール/クラス内部でのみ用いられるルーチンのヘッダ・コメントでも同様だ。そのソースファイルの読者が限られていて、共に共通するバックグラウンドを持っている場合、内部ルーチンのヘッダ・コメントは1行コメントであることが多いし、時にはヘッダ・コメントが書かれないこともある。一方、不特定の人に読まれ、メンテナンスされていくことを前提としたソースファイルの場合、内部ルーチンであってもそこそこ詳細なヘッダ・コメントが書かれることがある。

ステートメントについての「Why」は、意外な要素や注意すべき処理などの「意図(Why)」を説明するコメントだ。基本的には、シンプルなアルゴリズムを採用し、トリッキーなコードは避けるべきであるが、そこそこの大きさのアプリケーションを書いていると、どうしても他の読者の不意を突いてしまう部分が1〜2ヶ所は出てきてしまうものだ。チーム・レビューで他のメンバーからより良い代替案が出ることも多いのだが、どうにもならない場合には、注意を促すコメントを残すことになる。

ステートメントについてのコメントの重要度は「How < What < Why」だ。実際のコメントの分量は、多くの比較的マシな環境では「How < Why < What」だろう。ソースファイルの作者と読者に共通するバックグラウンドがあり、暗黙知によるコメントの省略が可能な環境では、コメントの分量は「How < What < Why」に近づいていくだろう。

では「データ構造」についてはどうだろうか? データ構造は、一度適切な構造が採用されてしまえば、あまり変更は発生しない。データ構造に変更が発生する際は、関連するソースコードを巻き込んで大々的に実装し直す必要があることが多い。例え適切なインタフェース抽象化することでモジュール/クラス外部への変更の波及を抑えられたとしても、モジュール/クラス内部はアレコレ手を入れなくてはならないだろう。つまり、データ構造に変更が発生した時点で、コメントによる「記述の二重化」とか悠長なことを言っている暇など無くなってしまう。どうせ大掛かりに変更することになるのだから、実はあまり二重化の弊害について気にする必要はない。

むしろ現実には、データやデータ構造についてのコメントは足りないことが多い。

プログラム書法』に曰く、「データの割り付けかたについての解説をつけよう」である。

プログラムに解説をつけるためにも、もっとも効果的な方法の一つは、単にデータの割り付けかたをくわしく説明する、というものである。おもな変数について、その値としてはどんなものが可能かを示し、それが変って行くようすを説明すれば、それだけでプログラムの解説は、ずいぶん進んだといってよい。

この指針に従うと、例えばクラスや構造体によって抽象化されたデータ構造の中身や、インスタンス変数のような生存期間の長いデータについて、HowやWhat(ときにはWhy)レベルのコメントが足されることになる。

ステートメントとは異なり、データ構造(または生存期間の長いデータ)については、HowやWhatのコメントを書いても許される側面がある。というのも、データやデータ構造の変化の様子は、システムやモジュール/クラスのライフサイクルにもとづく「計算の動的構造」をつまびらかにしなくては分からない要素だ。調べるためには、データやデータ構造を参照・変更する全てのステートメントを時系列順に確認する必要がある。はっきりいって、非常に面倒だ。しかしコメントで記述されていれば(そしてそれが正しいなら)、ステートメントを確認する必要はなくなる。

つまり、データやデータ構造については、例えHowレベルの内容であっても、実質的にはWhatレベルの要約コメントとして働くのである。

データやデータ構造についてのコメントの量は、モジュール/クラスの粒度や切り分け方、または使用する言語によって変化しやすい。

例えばモジュール/クラスの粒度が小さく、単一のコンテクストのみ扱うシンプルなものであるなら、その内部のデータ・データ構造についてのコメントは少なくなるだろう。しかし粒度が大きく、複数のコンテクストを抱えたモジュール/クラスでは、データ・データ構造についてのコメントは多くなるだろう。

Fileクラス、CSVパーサ・クラス、vectorのような動的配列クラス、これら3つを用いたデータファイル読み込みクラス――という風に4つに分かれているなら、それぞれの中身は比較的シンプルだろう。しかしこの4つの処理を全て自前で行おうとする単一のクラスは複雑だ。内部のデータ・データ構造のコメントも多くなるだろう。つまり、そういうことだ。

ステートメントもデータ・データ構造も、HowやWhatのコメントについては、コメント以外の他のメディア代替される余地がある。例えば、独自の通信プロトコルについて、仕様書が整備されているなら、わざわざ詳細をコメントに書く必要はないだろう。参照すべきドキュメントについてコメントするだけで十分だ。もちろん、仕様書が無いのならコメントでプロトコル形式(もしくはデータの例)を書いておくべきだろう。

ここまでの議論を元に、ソースファイルのコメントを削ったとしよう。後には次のようなコメントが残ることになる。

  • 「ファイルのヘッダ・コメント(What)」
  • 関数メソッドのヘッダ・コメント(What)」
  • ステートメントに関するコメント(What/Why)」
  • データ・データ構造に関するコメント(What/Why)」

さて、ここからさらにコメントを削ることは可能だろうか?

ここからは、使用する言語の抽象度・そのソースファイルの性質・作成者と読者の間の「暗黙知」の問題となってくる。

例えば、解くべき問題に合致している抽象度の言語を用いている場合、コメントを削りやすい傾向にある。タブ区切りファイルの第2フィールドの数値を加算した結果を求めるAWKスクリプトなら、ファイルのヘッダ・コメント1行と、処理するレコードのフォーマット(もしくはレコードの例)についてのコメント1行で十分だろう。しかし、C言語で自前で解析するというのなら、いくつかの関数を定義・実装することになり、各関数のヘッダ・コメントが1行ずつ必要となるだろう。

ソースファイルの性質の影響は、例えば「自社開発アプリケーションコンポーネントの一部で、概ね固定されたメンバーで管理しているソースファイル」なのか、「内製ライブラリで、ライブラリ開発部隊がメンテしていて、社内の複数のプロジェクトにて組み込まれるソースファイル」なのかによって、公開インタフェース(CやC++でいうヘッダファイル)のコメントの書き方は異なるし、内部ルーチンのコメントのスタイルも違ってくるだろう。メンテナンスする人が限られていて、メンバーも固定されがちで、外部への露出が少ないならば、コメントは簡素となる。一方で、不特定多数の人に公開されるライブラリのソースファイルでは、問い合わせを減らすためにドキュメントを充実させる傾向にあるので、コメントをドキュメント代わりにしている環境では、コメントも充実した内容(≒API仕様文書)となる。

ソースファイルの性質の件は、ソースファイル作成者と読者の間の「暗黙知」の問題と絡んでくる。メンバーがある程度固定された環境では、明示的・暗黙的にかかわらず、例えば「○○の場合は××のスタイルで書け!」のようなお作法があったりする。この環境に適応すると、ソースコード中の「××のスタイル」を目にした時に「ああ、ここは○○なのだな」と逆引きが容易になる。

名前の付け方やインタフェース定義の流儀など、明快かつ合理的な作法(型)が存在し、皆が作法に従っているなら、コメントするまでもなく暗黙のうちに理解できてしまう部分が生じるだろう。*5

暗黙知」が許されるか否かや、どこまで許されるかという問題は、当然ながら環境に依存する。メンバーが固定されていて、レベル差がそれほど大きくなく、書かれたコードがメンバー同士での利用に留まるなら、暗黙知にもとづいてコメントを削ってもあまり問題にはならないだろう。むしろメンバー的には冗長性が排されるので歓迎されるかもしれない。なぜなら、彼らからすれば「当たり前の常識」なのだから、わざわざ書くほどのことではないからだ。しかし、不特定多数の人が読むなど、どうしても読者のレベル差が広くなり、それに対処する必要があるのなら、逆に「暗黙知」をコメントで提示することになるかもしれない。

コメントの少ないソースファイルについて、個人的に良い例だと思っているのが、以下の『プログラミング作法』のサンプルコードだ。

PythonRubyなどからすれば低水準なCやC++を使っている、という側面はあるが……お仕事で書くコードで、メンバーが少数かつ固定された面子で、外部への公開をあまり考えなくてよいのなら、ここまでコメントを削っても許されるだろうと思う。

一方、私は普段、不特定の人が使用するライブラリの開発に携わることが多い。なので、コメントは冗長気味に書く癖がついている。以下のコードが、その雰囲気を表している(英語は間違っているので注意)。

(大御所のコードと比較すると、自分のコードの下手さ加減がよく分かる)

もちろん、自分用の小ツールなら、コメントは少ない(以下、Go言語)。

時には細かいコメントがほとんどないソースファイル(以下、Perl)もあるが……実は、暗黙のうちに「SNTPのパケットフォーマットの知識」を読者に要求している。

コメントを減らすことは、ある水準までは技術の問題だ。しかし、ある時点から次第に技術ではなく環境・文化の問題となっていく。

(本当に開発者のためになる)仕様書が存在し、しっかりメンテナンスされている環境ならば、開発対象についての高水準の記述について、コメントで書かれることはない。仕様書を参照すれば済むからだ。しかし、そうではない環境では、仕様書の代わりにコメントで記述されるかもしれない。

不特定多数の人が利用するライブラリを開発していて、API仕様書を書かないのならば、API仕様をコメントで記述することになるだろう。もしかしたら、JavaDocDoxygenを利用して、コメントからAPI仕様書を生成しているかもしれない。一方で、少人数で開発中のコンポーネントの一部で、作者も使用者もチーム・メンバーで、特に外部に公開することもないのなら、インタフェース部分のコメントは簡素となるだろう。それで困る人はいない。

さて、あなたはどの環境の人ですか? 相手はどの環境ですか? で、あなたの批評は、相手の環境に合致してますか?

*1:システムの肝の部分のコードを覗いた人への警告にはなるので。とはいえ、おそらく私でも「もうちょっとなんとかならんかったものか……」とため息をつくと思う。

*2バージョン管理システムが無い場合、ChangeLogのような「別のファイル」とdiff(≒ソースの差分を参照できる何か。patchでも、ソース一式のアーカイブでもよい)で履歴を管理すべきだが、手動での管理が面倒なため、差分の粒度が大きくなってしまいがちだ。CSVSubversionのような集中型バージョン管理システムの場合、運用など諸々の都合でコミットの粒度が大きくなってしまうことも多い。Gitのように比較的気兼ねなくコミットできる環境が整ったことで、細かな変更単位でコミットしてログを残すことが可能となってきたように思う。

*3:私自身の過去の記事での記述も含む。

*4:これ、『リーダブルコード』では若干嫌われている気がしないでもない(P.68)のだが、使ってしまうことにする。

*5:これが、アセンブラBASICの頃から時が止まっているかのようなクソな作法を強制される環境だと地獄なのだが……。