少し前にGoogleの論文で「引数による脆弱さの発見 (Detecting Argument Selection Defects)」がTwitterで話題になっていました。
Googleの2億行のソースコードを解析した結果、関数に渡す引数の順番を間違える系のバグは、引数の個数が6個以上になったときに著しく増えるので、引数の個数は5個以下が望ましいことがわかったという話。https://t.co/R5yy31pDyu
— Kentaro Hara (@xharaken) 2018年11月21日
この論文ではメソッド呼び出しの引数によって引き起こされるバグについて着目しており、例えば同じ型の引数が複数あると引数の順序を誤ったり異なった値を渡してしまうことによるバグが挙げられています。
例えばJavaの場合だとnullを誤って渡してもコンパイルは通ってしまうため、バグに気付きづらかったりしますよね。
他にも引数の順序を間違えやすい変数名などもありそうですよね。
論文では「actual, expected」「height, width」が頻度が高いとして紹介されていました。(普通は逆順)
未然にバグを生みにくいAPIをデザインする手段として同じ意味のパラメータの順序は統一すること(actual, expectedなどがそうですね)、コンストラクタの引数は継承関係にある親子で統一することなどが挙げられています。
実際にJava 10 b05のConcurrentHashMapにはコンストラクタの引数の順序を誤って定義したせいでバグがあったことが紹介されています。1
そして同様にバグを生みにくくするには引数の数を5つ以内に収めるべき、という提案がされています。
数が多ければここまでに紹介したバグを踏みやすくなるため、感覚的にも納得できる人が多いのではないでしょうか。
Googleの論文ではJavaのプロジェクトについて調べていますが、この記事ではそれ以外のプログラミング言語の標準ライブラリについて引数の数に何らかの特徴がないかというのを調べてみました。
調査方法
各言語のAPIドキュメントをざくっとパースして、関数名と引数をCSVとして出力しました。
APIドキュメントのスクレイピングによる大量アクセスを避けるため、ローカルでのAPIドキュメント参照が可能な
Dashの各言語のDocSetをパースして調査しました。
HTMLのパースに便利なNokogiriや正規表現を使いたかったため、集計はRubyで行いました。
集計結果はGoogle Spreadsheetで公開しています。
集計に利用したソースコードはMITライセンスで置いておきます。
調査結果
今回はJavaの他にPHP、Ruby, Goについて調査しました。
なお、主に調査を楽にするためこの調査結果にはいくつかの制限があります。
- 省略可能およびデフォルト値を持つ引数、可変長引数はいずれも1としてカウントしています。
- Rubyの中置演算子は本来引数の数は2となるべきだと思いますが0にカウントしています。
- Goは公開メソッドだけではなくすべてのメソッドをカウントしています。
- 継承やオーバーライドによる重複の割合は言語によって違うと思われますが無視してAPIドキュメントにあるメソッド・関数をカウントしています。
詳しい値はスプレッドシートのほうを参照ください。
PHPは3引数以上で他の言語よりも割合が多くなっているのがわかります。
Rubyは引数0のものが多いのが特徴ですね。
Goは思っていたより3引数、4引数の割合がありました。ただ5引数以上はだいぶ少ないです。
JavaはPHPと傾向が似ている?ような気がします。
PHP
PHPの引数でいうと implode の「歴史的な理由により、引数をどちらの順番でも受けつけることが可能です。」が思い出されます。
筆者は最近PHPでのプログラミングをやっていないのでよく知らないのですが、バージョン7での性能向上を始めとした改善、Laravelの台頭により以前のような悪い評価は減ってきているように感じています。
一番引数の数が多かったのは trader_sarext 関数で省略可能なものを含めて12あります。
Ruby
Rubyが他の言語に比べて0引数のメソッドが多いのは、中置演算子を引数0としてカウントしているのもあるとはおもいますが、すべてがオブジェクトであり操作対象を一つ選択省略できるのが影響しているような気がします。
一番引数の数が多かったのは OpenSSL::PKCS12 create で省略可能なものを含めて10あります。
Go
動的型付け言語を2つ入れたので、Googlerが自分たちのために作ったような言語なのでAPI設計思想にもなんらか現れているのでは、と思って静的型付け言語の中から選択しました。
一番引数の数が多かったのは [math/bigのlehmerUpdate](https://golang.org/pkg/math/big/?m=all#lehmerUpdate) で11あります。公開メソッドの中で一番多いのは iconvgのSetEllipticalGradient で10あります。
関係ないんですけどmakeの引数、lengthとcapacityの順序が時々よくわからなくなって「省略可能だからcapacityが3番目」っていう面倒な覚え方しているの私だけでしょうか。
Java
Javaを選んだ理由はGoogleの論文でメインで調査の対象にしていたため、他の言語との比較を見るためです。
一番引数の数が多かったのは javax.swing.SwingUtilities#layoutCompoundLabel で12ありました。
Swingは古いのもあると思うんですが、どうしても引数の数が多めになりますね。引数の型を見るだけでは呼び出せる自信が全くありません。
終わりに
この記事ではGoogleの論文に触発され、プログラミング言語ごとの特徴を標準ライブラリの関数・メソッドの引数の数から見てみました。今回調べたのは引数の数ごとの割合でまとめてしまっているため、わかったのはオブジェクト指向言語であるかどうか否かの差とかざっくりしたことだけでしたがそれでも言語ごとになんとなく特徴があることがわかりました。
他の言語についても調べてみるといいのかもしれないです(この言語との比較がおもしろいんじゃないか、というのがあればコメントください)。あとは「同じ型の引数の数が多い言語」とか調べてみると面白いかもしれません。