前回の記事で少し触れた機械可読性指向プログラミングについて、今回は掘り下げてみたい。
まず、機械可読性が求められる背景を確認した後、その定義と定量的評価方法を模索し、最後にいくつかの機械可読性向上パターンとアンチパターンを見ていきたい。
機械可読性の高いコードが求められる背景
TL;DR, even if clean
社会が複雑になればなるほど、それを支えるインフラも複雑化する。
ソフトウェアでもそれは例外ではなく、プロジェクトが巨大化・複雑化すれば、
その分だけコード量も増える。
OSは数千万行、Google検索エンジンに至っては20億行ものコードがあると言われている。
そこまで行かなくても、ことにオブジェクト指向言語のプログラムではクラスファイルの氾濫も起こりやすく、
たとえクリーンコードで人間可読性を高めても、何がどこにあるのか、人力だけで探すのは困難となる。
そこで、必然的に「できるだけ読まずにコードを探す」必要が出てくる。
その役割を担うのは、コンピューターである。
Not as good as Google
SEO特化サイトの出現で、Googleの検索結果の質が下がっているという指摘があるが、
それでも世界のネット検索の9割以上のシェアを誇っているGoogleの検索エンジンは、
世界で最も優れた検索機能を誇るシステムの一つであることに変わりはないだろう。
だが、Googleはネットでしか稼働せず、閉鎖的な組織内ネットワークやローカルPCでは機能しない。
必然的に、検索するならIDEやテキストエディター、バージョン管理システムのものを使うことになるが、
これらのサービスで使える検索機能は比較的単純である。
つまり、Googleにしかできないような高度な検索は、プロジェクトのソースコードについてはできないか、
できたとしても極めて困難であり、機械に読みやすいコードでないと検索しにくくなるのである。
同じ「規則」に沿ったコードの増殖
完全に同一なコードであれば共通化によって増殖を阻止できる。
だが、「似ているが同じではない」コードはそうはいかない。
クラス分けして整然と整えたとしても、そのクラスの数だけは似たようなコードが並びうる。
またクラス分けの有無に関係なく、命名規則などのコーディング規約がある場合は、規約に則った規則的コードが増殖しやすい。
前回も書いたが、規則的なコードの自動生成は、コンピューターが得意とする領域である。
換言すれば、どのみち規則的なコードが増殖しやすいのであれば、
もう一歩進めて、「コンピューターが生み出しやすい」コードを考えることで生産性を向上させられるのである。
機械可読性の定義
機械可読性の定性的定義は比較的容易であり、規則性が高いことと、規則がシンプルであることの2点に集約される。
規則性が高いこと
コンピューターはとかく例外に弱い。
例外が1パターンあるごとに、その例外への対応ルールを増やさなければいけない。
プログラムのExceptionなら工夫して一か所で処理させることは可能だろうが、
例外「条件」の場合はそう単純にはいかない。
例外が多すぎて、もはや規則が成り立つとは言えない状態が機械にとって最悪であるのは、容易に想像できるだろう。
だが、逆に、規則的な計算なら、黎明期のコンピューターでさえ人間よりもずっと速くできるのだ。
規則がシンプルであること
1, 2, 3, …と来たら、次は何だろうか?
多くの人は4と答えるだろうし、高校数学の数列の問題であれば、それが正解とされるだろう。
だが、それは、関数f(x) = ax^3 - 6ax^2 + (11a + 1)x - 6aにおけるa = 0の事例に過ぎず、
このルールを満たせばa = πの場合だろうが、a = iの場合だろうが、何でも良いのである。
(更に言えば、上記の例より高次の関数を用意して、もっと複雑な例を作ることすらできる)
このように、規則的であっても、複雑な規則を考えることはできる。
だが、実際に機械可読性の観点でそれに成功しているのは、高度な検索エンジンだけだ("Not as good as Google")。
シンプルな規則(ex. 文字列の完全一致)の方が、複雑な規則よりも検索で対応されている可能性も高いし、
自動生成の観点でも対応方法を考えやすい。
つまり、機械にとって読み書きしやすいのである。
(「書き」に言及したのには意味があり、後述する)
定量的定義
自然言語の可読性の定義でさえ決定的なものはなく乱立しているくらいだから、定量的な定義は容易ではない。
ただ、いくつかの定量的かつ測定可能な指標を考えることはできる。
1. 与えられたコードを完全自動生成するのに必要な規則の数
2. 上記規則全てに対応するコードのうち、最小となる部分集合のコード量(絶対量もしくは総コード量に占める割合)
3. 規則的な可変部分の個数と、その入れ子構造の深さ(平均も良いが、ボトルネックの考え方をするなら最大深さの方が良いかもしれない。なお、例を挙げるなら、「itemN」(N=1~10)という表記の場合は、可変部分1か所、入れ子構造の深さは1となる。)
1については、荒技だが、「コードのコピペを出力する」「コードファイルを読み込んで順に出力する」という究極のシンプルルールが存在し、
このルールを使うとどんなプログラムでも必要な規則数は1にできてしまう。
だが、その方法では常に2がコード量そのものとなり、また可変部分の個数もコードの文字数分となるため、他の指標は惨憺たるものになる。
つまり、規則の設定の仕方でこれらの指標は変わってしまうのだ。
そこで、一意的な定義として、以下のような形を取りたい。
ソースコードの機械可読性とは、ある同一の評価手法によって以下の積を最小化した場合の数値で表現される。
数値が小さいほど、機械可読性は高い。
(与えられたコードを完全自動生成するのに必要な規則の数)
×(上記規則全てに対応するコードのうち、最小となる部分集合のコード量、もしくはその総コード量に占める割合)
×(規則的な可変部分の個数)
×(可変部分の入れ子構造の深さの平均、もしくは最大値)
また、一部例外を除いた指標を考えたい場合、以下の形で、N%機械可読性を考えることも許される。
Nを大きくしても小さい数値を維持できるほど、機械可読性の高い領域は大きいと言える。
(与えられたコードのN%以上を自動生成するのに必要な規則の数)
×(上記規則全てに対応するコードのうち、最小となる部分集合のコード量、もしくはその総コード量に占める割合)
×(規則的な可変部分の個数)
×(可変部分の入れ子構造の深さの平均、もしくは最大値)
もちろん、この定義には、最小化=最適化計算の困難さや、未検証の数学的厳密性などの課題はあるが、
ここでは何がやりたいかという本質的な部分を示すことが目的であるので、
定義の厳密さや正確さ、計算容易性のブラッシュアップは興味のある方にお任せしたい。
なぜ「可読性」なのに「自動生成」?:検索可能性と自動生成可能性の等価仮説
ここで、ようやく可読性の問題でありながら「書く」ことに触れた理由に入ることができる。
これまた厳密に証明した訳ではないが、
機械が見つける(読む)ことができる「検索可能性」と、
機械が書くことができる「自動生成可能性」は、
等価ではないかと予想している。
固定的な文字列は容易に検索も生成もできるが、正規表現を使うべきであればその分だけ検索処理にせよ自動生成処理にせよ、実装難度は上がる。
また自然言語ともなれば、ごく一部の高度なサービスだけがそれなりの精度の検索を取り扱えて、書くことに関してはまだまだ発展途上である。
いずれも規則の複雑さが上がるほど、機械処理の難易度も高くなる好例だといえる。
故に、検索しやすいコードは自動生成しやすく、自動生成しやすいコードは検索しやすい、という仮説を立てて、
両者を同一視できるものとして扱っているのである。
機械可読性向上のパターンとアンチパターン
デザインパターンなどに比べればそれほど難しくない内容だと思うが、機械可読性向上のために、いくつかの考えられるパターンとアンチパターンを考えてみたい。
対応性の良さパターン
ある対象と、それに紐づけたい対象の紐づけが、記載だけで明らかなパターンは、機械可読性が高いと言える。
例としては、以下のようなものが考えられる。
・データベースのカラム名(string)と、エンティティクラスのプロパティ名が容易に一対一変換できる場合
・仕様書の適切な表と、その各要素に対応するソースコードのプロパティ等が容易に一対一変換できる場合
規則的構造パターン
ある対象の構造が規則的なパターンは、機械可読性が高いと言える。
例として、以下のようなものが考えられる。
・出力レイアウトに沿った規則的な要素配置で記されたコード
・クラス名からIntellisenseなどで示唆されるインスタンス名をいじらずにそのまま使っているコード
識別容易性パターン
異なる要素(変数名やメソッド名、クラス名など)が容易に識別可能であることは、検索可能性も自動生成可能性も向上させる。
対応性の良さにもつながる。
長すぎる・短すぎる識別記号アンチパターン
識別容易性を確保するために名前を長くし過ぎると、その分だけ正確に一致させる必要が発生する(特にワイルドカード検索使用不可のAzure DevOpsのような、部分一致検索ができないサービスの場合に顕著)。
かといって、変数をaなどのように短くし過ぎると、他と区別できなくなって、検索したら無駄な情報まで混入することとなりかねない。
これは人間可読性とも共通なので、特に異論はないだろう。
表記ゆれ・ミスタイプアンチパターン
表記の揺れやミスタイプは、多少の苛立ちの元になっても、人間可読性はそこまで落とさないだろう。
だが、機械可読性はそれだけで大いに落ちてしまう。
・訓令式ローマ字とヘボン式ローマ字が混在
・Numberedとするべきところが何故かNumberdになっている
・大小文字の並びがバラバラ:WhatsoeverとWhatSoEverが混在する、など
語彙・言語不統一アンチパターン
語彙や言語の不統一は、人間可読性もそれなりに落とすが、それ以上に機械可読性を落としてしまう。
・和英混在:NumberとBangouが混在
・英数混在:Item1とItemOneが混在
・Id、Code、Numberが意味の区別が曖昧なまま混在
・AbbreviatedFormatとShortNameがどっちも「略称」を指す語彙として混在
「人間のための例外」アンチパターン
コーディング規約などの人間が作ったルールには、往々にして人間のために設けられた例外が存在する。
これらの例外は、ルールの曖昧さの元となり、表記ゆれや語彙・言語不統一などの他のアンチパターンの誘発要因となるのみならず、
それ自身機械可読性を落とす要因になる。
プロジェクトが大きく、"TL;DR, even if clean"状態になっているのであれば、
多少ばかり人間可読性を犠牲にしてでも、機械可読性のために例外規則を許すべきではない。
どうせ人はそこまで読まないのだから。
例としては、以下のようなものが考えられる。
・原則英語だが、難解な専門用語だけはローマ字OK
→専門用語の一部だけが容易に翻訳可能なら?こうして和英混在の発生源になっていきかねない。
・原則正式名称だが、一部は略称・省略表記OK
→どこでどっちが使われているのか分かりにくくなる。
また、どんなにそのスコープが短くとも、略称の混在は識別容易性の観点ではノイズでしかない。
終わりに
一本のブログ記事としては長すぎだと言われそうだが、私の中ではあくまでも「軽く」機械可読性の問題を考えてみたつもりである。
まだまだ詰め切れていないところも多いが、ここまでの内容を要約して終わりにしようと思う。
プロジェクトの巨大化が検索可能性と自動生成可能性を求めるようになったが、
それらは機械可読性として、シンプルな規則で高い規則性を以て記述できることを指標とする形に定式化できる。
今回はその実際の計算は踏み込まなかったが、代わりにいくつかの機械可読性向上のためのパターンとアンチパターンを挙げてみた。
尤も、この問題は容易に結論付けられるものでもないので、今後の発展に期待したい。
ただ、機械可読性指向プログラミングは、自動生成や、それをバックでそのまま実装にしてしまう
ノーコード・ローコードの路線の充実・拡大なども含めた広範な可能性を秘めており、
プログラマーの生産性向上にも直結するだろう、とだけ予言しておこう。