Cスタイルでも短いコードが書きたいってのはどうだろうか

ちょっと奥さん。MLでMLコンパイラ(サブセットだけど)を書くと2000行で書けるらしいですよ。


と、いうわけで、なんか面白そう、と思って見てみたんだけど、これは、ちょっと卑怯じゃないか。何が卑怯かというと、ソースコードに空行がほとんど無い。たまーに関数の間に一行空いてるくらいで、ほとんどぎっちり詰めてある。
そんなので「関数型言語C++よりも生産性が高い」とか言うの?それは反則なんじゃないのかっ!?


と、まあ、そんなことは別にどうでもいいとして、おもしろいのは、このソースコード、空行がほとんどないのに、可読性に問題が無い、という点である。C/C++でそんなことしたら、ちょっと痛々しいソースコードになってしまうだろう。また、逆に、OCamlソースコードで変に空行を入れたりなんかすると、スカスカ感がして、読みにくいかもしれない。Ocamlだけでなくて、Schemeもその感じがあるかもしれない。Haskellはよくわからんけど、多分、似たようなもんだろう。
これは、どこが違うというのだろうか。と、考えるのは面白いかもしれない。

そこで、Cスタイルのコード好きとして、ここは、そこらへんについて考えて、無理矢理Cスタイルに、そういうのを混ぜてみよう、という話。


まあ、そういうネタなんだけど、空行を入れなくてもソースコードが読みやすい、となれば、一個のエディタに含められるソースコードの量が増えて作業効率が上がるだろうし、しかも、可読性がよくなる、とも考えられるので、実益もあるかもしれない。


まず、どこらへんから違いがくるか、を考えるために、ちょっとしたコードをOcamlとCで書いてみる。ネタは「ファイルの中身に書いてある時刻と、ファイルの変更時刻を比較する」と、いうもの。

n,12:30

こんなファイルがあって、このファイルの変更時刻が12:30よりあとだったらOKと表示。で、最初の一文字が'o'だったらその反対、12:30より前だったらOKと表示。と、そんな感じ。
Ocamlで書いたのが、ml.ml。Cで書いたのがc.c


Cは、多分、読みやすさと短かさを考えると、このぐらいが妥当だろう。MLのほうは、よくわからん、もっと短くなるかもしれないし、もっと読みやすくなるかもしれない。MLのほうが、58行。Cのほうが133行。やってることは大差無い。ungetcを使ってないぶん、Ocamlのほうが汚い。んで、参考までに、Cのほうは行間詰めると、100行。


で、これについて考えてみることにする。

  • 塊のあとの空行

Cでいうと、ブロック。Ocamlのほうでいうと、長めのlet ... inの間。
僕の場合、Cだと、

nanika;

while ( ... ) {

}

nanika;

まずまちがいなく、こういうスタイルになる。んで、詰めると明らかに読みにくい、でCamlのほうが

let nanika = xx in 
let hoge = match xx with 
    | _ -> ...
    ....
in
nanika...

こんなふうになる。ここで、下から二行目のinの前や後に一行入れたりはしない。そして、これでも読みにくいわけではない。


空行の入れかたの違いに関しては、これが一番大きいだろう。
これは、Cの場合はインデントが左に行っても処理が続いてしまうので、どこが区切れかわかるようにしてほしい、と思うのに対して、OCamlの場合は、インデントが左にいったときに、そこで一区切りつくから、空行無くても大丈夫。といった感じだろうか。よくわからんな。思考とか認識の問題だと思う。


とりあえず、ブロックの終わりは区切れてくれるような構文にしてほしい。ってとこか。

  • if式

if が式として使える。

int x;

if ( value == OK ) {
	x = 8;
} else {
	x = -8;
}

return x;
if value == OK then 8 else -8

空行ではないけど、これは、影響が大きいかと。MLのほうが横に長いのも多分そこらへんの影響。
Cでも三項演算子で書けるけど、可読性でいうと、あれはあんまり使わないほうがいいだろう。


ifは式として使いたい、と、いったところか。


ん。なんか書くの面倒になってきたな…他にも若干あるんだけど、まあ、なんだかんだあって、そこらへんを考慮しつつ、Cスタイルを保つ、となると、僕が考えるのは、

  • ブロックとかifとかが値を持つ。
  • 型推論
  • 構造体をつくるのが楽。

こんな感じ。
ひょっとしたら、そこにあるのは僕の理想のカタチかもしれない!!


と、思って、それで、書いてみたら、全然駄目だった。
なんていう適当なオチ!!

read_2int( FILE *fp )
{
	let n1 = {
		let c = getc( fp )
		{
			if ( isdigit(c) == 0 ) { parse_error(); 0; } else c-'0';
		}
	}

	return {
		let c = getc(c)
		{
			if ( isdigit(c) ) n1*10 + (c-'0');
			else { ungetc(c,fp); n1; }
		}
	}
}

残骸。if文とif式を別物として扱えないと無理。それ以前になんか色々無理。文法とか以前に気持ち悪くて書けない。結局無意識のうちに空行入れてるし。何が違うんだ。


Cでも副作用をほぼ無しで書くことは不可能ではないから、そういうのが問題ってわけでもないだろう。まさか、ブレースだから駄目ってこともないだろうし。
LispとかOCamlなんかで書くと、自然と行間無しで書いてるわけだから、どっかで思考を切り換えてるはずなんだけど。それがどこかわからん。自分のことなのに。