関数の引数は一つだけでいい
関数は複数の引数を持つことができる。当たり前のことだ。ぼくたちはプログラミングを学び始めたころから、そう教わってきた。
add(a, b) という関数があれば、a と b を足した結果が返ってくる。シンプルで、わかりやすい。便利なので、一般的にもよく使われている。
けれども、ここでひとつ立ち止まって考えてみたい。この「当たり前」は、本当に最善なのだろうか。今回は、その常識を疑って、新しい提案をしてみたいと思う。
複数の引数がもたらす二つの問題
複数の引数を持つ関数には、二つの厄介な問題がある。
ひとつめは、引数の順番に依存してしまうことだ。
たとえば createUser(name, email, age) という関数があるとしよう。ぼくたちは、この順番を覚えていなければならない。もちろん、関数の定義を見に行けば型や変数名が書いてある。間違えることは少ないかもしれない。
しかし、誤った順序で指定すれば、それはバグになる。オプション引数が増えると、話はさらに厄介になる。
わかりやすい例がある。高機能なOSSライブラリでは、利用者の期待に応えるあまり、ひとつの関数に大量のオプショナル引数が並んでいることがある。ありがたい機能だ。けれども、ドキュメントを読み込まないと使い方がわからない。
ふたつめの問題は、仕様変更に弱いということだ。
引数を追加するとき、すべての呼び出し箇所を修正しなければならない。JetBrains製のIDEなら抜け漏れなく直せるかもしれない。けれども、確認は必要だし、影響箇所は少ないに越したことはない。
ちなみに、関数の戻り値を複数返してアンパックする場合も、同じ問題を抱えている。
オブジェクトを渡すという解決策
では、どうすればいいのか。
ぼくの提案はシンプルだ。オブジェクトか辞書を、引数として渡せばいい。
TypeScriptで書くと、こうなる。
interface CreateUserParams {
name: string;
email: string;
age: number;
}
function createUser(params: CreateUserParams) {
// ...
}
// 呼び出し側
createUser({ name: "田中", email: "tanaka@example.com", age: 30 });
この書き方には、三つのメリットがある。
まず、意図が明確になる。パラメータ名で意味を表現できるので、何を渡しているかが一目でわかる。IDEの補完も効きやすい。
次に、柔軟に拡張できる。新しいプロパティを追加しても、オプショナルにしておけば既存の呼び出しを壊さない。
そして、型安全性が高まる。TypeScriptならインターフェース、PythonならTypedDictで構造を明示できる。
戻り値についても同じことが言える。特別な理由がなければ、オブジェクトで返すべきだとぼくは考えている。
ただし、注意点がふたつある
この提案を実践するとき、気をつけてほしいことがある。
ひとつめ。プロパティだらけの関数は、そもそも分割すべきだ。
オブジェクトを渡せば順番は覚えなくていい。それはわかった。しかし、プロパティが大量に必要な関数には、別の問題がある。
関数がプロパティを受け取るのは、それを使って何かをするためだ。大量のプロパティを受け取る関数は、たいてい多くの責任を抱えすぎている。
引数をオブジェクトに変える前に、まず問うべきだ。この関数の責務はひとつか。分割して可読性を上げられないか。
ふたつめ。伝わるプロパティ名をつけること。
オブジェクトにしたところで、名前がわかりにくければ意味がない。プロパティ名だけで、何を渡すべきかわかるようにしよう。どうしても誤解を招きそうなら、JSDocで補足すればいい。
よくある反論に答える
ここで、読者からの声が聞こえてきそうだ。
「引数が1〜2個なら、従来どおりでいいのでは?」
たしかに、シンプルな関数では過剰かもしれない。けれども、将来の拡張を考えると、最初からオブジェクトにしておく方が安全だ。とくに公開するAPIでは、後からの変更は難しい。
「オブジェクトを作るオーバーヘッドは?」
モダンなJavaScriptエンジンでは、オブジェクトリテラルの生成は極めて高速だ。ゲームのフレームループのような場面を除けば、影響はほとんどない。可読性のメリットの方が、はるかに大きい。
まとめ
関数の引数は、基本的にひとつでいい。
ただ、その前に考えるべきこともある。プロパティが多すぎる関数には、疑いの目を向けよう。引数なしで呼べるシンプルな関数は、それだけで読みやすい。
責務を適切に分割したうえで、それでも複数のパラメータが必要なら、型定義したオブジェクトを渡そう。
ぼくたちの書くコードは、未来の誰かが読む。その誰かには、数ヶ月後の自分も含まれている。そのとき、引数の順番を思い出そうとして時間を使うのは、もったいない。