変数(variable)と値(value)

はじめてScalaに触れたとき、変数宣言(var)と値宣言(val)を使い分ける言語仕様に、なるほどなあ、と思った。簡単に言えば、変数(var)は再代入できて、値(val)は再代入できない。

プログラミングのスタイルとして、var宣言は命令的なプログラミング、val宣言は宣言的なプログラミングになる。どちらのプログラミングスタイルで書いているかを、varとvalで明示できるわけだ。

Javaだと言語の基本の仕組みはすべてが変数。final宣言をすることで再代入をコンパイルエラーにすることはできる。Javaは、C言語C++などの命令的なプログラミングの系譜の言語なのですべて変数(variable)というのは、とうぜんの言語仕様だった。

命令的なスタイルから宣言的なスタイルに

命令的なプログラミングでは変数(variable)を使う。宣言的なプログラミングでは値(value)を使う。

再代入可能な変数(variable)は、プログラムの実行時の状態が基本的には不定(不安定)になる。

それに対して再代入できない値(value)は、プログラムの実行時の状態が仕組みとして不変になり安定する。

宣言的なスタイルを選ぶ

プログラミングのアプローチとしては、命令的なスタイルより宣言的なスタイルのほうが良い。プログラムの実行時の挙動が安定することは、良いプログラムを書くために大きなアドバンテージになる。

変数(variable)と値(value)の使い分け

ScalaやKotlinのように変数(var)と値(val)を明示的に使い分けることができる言語だと、命令的なスタイルと、宣言的なスタイルを明示的に書き分けることができる。

Javaのfinal宣言

Javaだと、final宣言を使うことで、いちおう宣言的なスタイルを表明することはできる。ただしfinal宣言した変数が参照するオブジェクトの状態が変更可能であると、簡単に命令的なプログラミングに逆戻りしてしまう。

実際にやってみると

私自身は可能な限り宣言的なスタイルでプログラミングをしたいので、ScalaやKotlinでいろいろ書いてみた時は、変数宣言(var)はまったく使わず、値宣言(val)だけで書いた。

valだらけの気持ち悪さ

そうして書いたコードを眺めてみると、valだらけで、valという宣言がほとんど意味のないノイズに近い印象をうけた。その時思ったのは、何も宣言しなければ、デフォルトで値宣言(val)になり、どうしても変数宣言(var)にしたいところだけvarを記述する、という言語であればもっとよいのになあ、と思った。

final だらけの気持ち悪さ

Javaの場合は、もっとひどい。言語仕様としては、インスタンス変数・メソッドの引数・メソッドのローカル変数・クラス変数をすべてfinal宣言にできる。

しかし、宣言的なスタイルの表明ということで徹底的にfinal宣言を書きまくると、ScalaやKotlinのval宣言だらけよりも、もっと読みにくいひどいコードになる。わかりきった場所までfinalを書くことに「いったいなんの意味があるのか」と疑問に思うくらい、final宣言が邪魔に見えてくる。

Javaの場合、final宣言を徹底しないとfinalで宣言した参照先のオブジェクトで変数を上書きされてしまう可能性が高い。標準ライブラリで用意されているList・Set・Mapなどは、変更可能なオブジェクトなので、final Map map; というような宣言をしても、実質的には、宣言的なプログラミングではなく命令的なプログラミングのままになる。つまり実行時のmapの状態は不定(不安定)なままなのだ。

しかし、そこまでfinal宣言を書くことは単純に面倒だし、書けば書くほどコードが読みにくくなるのが現実。

じゃあどうするか

ScalaやKotlinだとvalだらけになるのは、言語仕様上しかたがない。ところが、Javaの場合は、別の選択肢がある。

finalを書かないという選択肢

それはfinalを書かないという選択肢である。

もちろん、言語仕様的にはfinal宣言をしていない以上、それは再代入可能な変数であり、見た目は命令的なプログラミングそのものである。

ただしプログラミングの方針あるいは約束事として、宣言的に書く、変数の再代入はしない、ということを大前提として共有できていれば、ノイジーで面倒くさいfinal宣言をまったく書かないことも選択できる。

ScalaやKotlinのように値(val)を取り入れるた言語では、宣言的に書くにはvalを書かざるを得ない。 しかし、値(val)の仕組みがないJavaの場合、それを逆手にとって、約束として再代入不可を共有してfinalは書かないというアプローチを選択できるわけだ。

ここらへんは、ふつうのぷろぐらまのirofさんのこの記事も参考になる。

finalを付けるのをやめてみた(日々常々)

final書かずに、宣言的なプログラミングスタイルで意思統一しよう

私は、finalを書いても命令的なプログラミングのままのコードをたくさん見てきた。

final宣言の記述を強制しても、コードを書く人間の頭は命令的なプログラミングスタイルのままだということ。

そこの発想を切り替えるには、final宣言の記述のルール化よりも、変数に再代入をしない、そうすることでプログラムの挙動が安定するし、プログラムのロジックがシンプルになる、という宣言的なプログラミングスタイルの考え方とやり方を粘り強く伝え、それを共有知にすることに時間とエネルギーを使う方が費用対効果が高いと思って、日々の開発に取り組んでいる。

実際のところ、表面的にfinal宣言だらけで書いているチームよりも、final宣言は書かないが、変数には再代入をしないプログラミングスタイルを共有できているチームのほうが、読みやすく質の高いコードを生み出せている手ごたえがある。

f:id:masuda220:20211128085518j:plain