忙しい人のための結論
ソースコードの可読性を高めるために「真偽値型の変数名は主語を含めた文章にしよう」という提案がこの記事の主旨です。主語を含めた文章というのは例えば「userIsHogehoge(ユーザーはほげほげである)」のような形式です。userIsHogehogeの最初のuserの部分が主語であり、全体として文章になっています。
そもそも
以前、Reactの公式チュートリアルを見ていたときに「xIsNext」という名前の変数(厳密にはオブジェクトのプロパティでしたが、説明の都合で一旦は区別せず話を進めます)に出会いました。これは「xが次の番であるか」の真偽値を管理する変数なのですが、それまで変数名は名詞であるべきと思い込んでいた自分にとって、強い違和感があったのを覚えています。意図が理解できず、そのときはスルーしました。
一方、当時は真偽値型の変数を命名する際にいつも困っていました。「変数名は名詞」に則って名詞を充てようとしても適切に命名できず、インターネット検索を参考に「isHogehoge」のような形式で命名していました。
しかし「isHogehoge」のような主語を含まない形式だと後述する問題が解決できません。なんとなくもやもやしたまま特に答えを出すこともなく時が経ちました。
そしてある日「xIsNext」であればそれを解決できるということに気付きました。「変数名は名詞」という認識を「変数名は名詞または文章」に改めることにしました。
コード例
真偽値を使用する典型的なシチュエーションとして、if文で使用する際の例を挙げます。三項演算子の場合でも結論に影響はありません。
以降のコードは特筆した場合を除いてPythonです。Pythonのコードに関しては真偽値に関する部分のみ型アノテーションをつけています。命名規則についてはPEP8で推奨されるスネークケースではなく、他の言語で最も一般的であろうキャメルケースを採用しています。
主語を含まない場合
主語を含まない「isHogehoge」のような形式だと
user = "hogehoge"
isHogehoge: bool = user == "hogehoge"
if isHogehoge:
print("ユーザーはほげほげです")
if isHogehoge:
の箇所が「if is hogehoge」という、英文としては不自然な表現になってしまいます。また主語がないため、何が「ほげほげである」かがわからず、情報量として不足しているように感じます。
主語を含む文章の場合
主語を含む文章の場合だと
user = "hogehoge"
userIsHogehoge: bool = user == "hogehoge"
if userIsHogehoge:
print("ユーザーはほげほげです")
if userIsHogehoge:
の箇所が「if user is hogehoge(もしユーザーがほげほげであるのであれば)」という英文に近い自然な表現になります。また主語が伴っているので意味が明瞭です。
まとめ
以上、表題の「真偽値型の変数名は主語を含めた文章にしよう」という提案の理由を、主に実利的な必要性から論じました。
ただ、そもそも真偽値型の変数に入るのは「○○が××であるか」という命題への回答です。命題は通常、文章の形式をとります。「はい」か「いいえ」で答えられるものは普通は文章ですよね1。なので、あるべき論としても真偽値型の変数名は主語を含めた文章にするのが正しいのだと思います。
以降は補足および蛇足です。長くなってしまいましたので時間があるときにお読みください。
補足
オブジェクトのプロパティ名の場合
ここからは「変数」と「オブジェクトのプロパティ」を明確に区別して論じます。
オブジェクト(クラスをインスタンス化したものやJavaScriptのオブジェクト型など)のプロパティ(言語によっては「メンバ変数」)名を文章にすると、呼び出す際に主語が重複するので不適切になります。コード例を示します。
class User:
def __init__(self, name, age):
self.name = name
self.age = age
self.userIsAdult: bool = age >= 18 # userIsAdultが文章になっている
taro = User("Taro", 18)
jiro = User("Jiro", 17)
if taro.userIsAdult: # taroとuserで主語が重複している
print(f"{taro.name}は成人です")
else:
print(f"{taro.name}は未成年です")
if jiro.userIsAdult: # jiroとuserで主語が重複している
print(f"{jiro.name}は成人です")
else:
print(f"{jiro.name}は未成年です")
if taro.userIsAdult:
やif jiro.userIsAdult:
の部分は、できれば「if taro is adult」「if jiro is adult」という英文に読ませたいですよね2。なのでオブジェクトに真偽値のプロパティを設けるのをやめ(つまり上記の例のuserIsAdultを削除し)、次の例のように真偽値を返すメソッドを追加し、その名前を「isHogehoge」の形式にするのがいいと思います。
class User:
def __init__(self, name, age):
self.name = name
self.age = age
def isAdult(self) -> bool: # 「isHogehoge」の形式で命名している
return self.age >= 18
taro = User("Taro", 18)
jiro = User("Jiro", 17)
if taro.isAdult(): # 主語が重複していない
print(f"{taro.name}は成人です")
else:
print(f"{taro.name}は未成年です")
if jiro.isAdult(): # 主語が重複していない
print(f"{jiro.name}は成人です")
else:
print(f"{jiro.name}は未成年です")
これで「if taro is adult」「if jiro is adult」という英文に読ませることができるようになりました。しかもプロパティへの直接のアクセスがなくなり、メソッド(上記の例ではisAdult)を通じてインスタンスの状態を取得するようになったことで、オブジェクト指向の「プロパティにアクセスしたいときは直接アクセスするのではなくgetterメソッドを通じてアクセスするようにしよう」という考え方とも整合的であると思います(isAdultが厳密な意味でgetterメソッドであるかは微妙なところですが、少なくとも「プロパティに直接アクセスすべきでない」という趣旨においては実質的にgetterメソッドとみなしていいと思います)。
蛇足
ここからは記事の本題から逸れるため蛇足としています。
トップレベルで真偽値を返す関数をどう命名すべきか(未解決)
(追記。D言語やNimのUFCSで解決可能。以下は改稿予定)
この記事の本題は「トップレベルで真偽値を返す変数をどう命名すべきか」についてでした。では「トップレベルで真偽値を返す関数」についてはどう命名すべきでしょうか。オブジェクトのプロパティ名の場合のようにクラスのメソッドとしてisHogehoge()と命名するのは問題ないとして、クラスのメソッドではなくトップレベルでの関数の場合は問題になります。試しに「isHogehoge」の形式で命名したコード例を示します。
def isAdult(user) -> bool: # 「isHogehoge」の形式で命名している
return user["age"] >= 18
taro = {"name": "Taro", "age": 18}
jiro = {"name": "Jiro", "age": 17}
if isAdult(taro): # 英文としては不自然
print(f"{taro['name']}は成人です")
else:
print(f"{taro['name']}は未成年です")
if isAdult(jiro): # 英文としては不自然
print(f"{jiro['name']}は成人です")
else:
print(f"{jiro['name']}は未成年です")
こちらについては解決策が現在も思いついていません。例えばisAdult(taro)
をtaro.isAdult
のような順番でも書けるのであれば解決しますが、そのような言語があるかどうかは不明です。関数名に文章を充てるのを許容してuserIsAdult
としても、呼び出す際にuserIsAdult(taro)
となるだけで自然な英文とはなりません。良いアイデアをお持ちの方はお知らせください。
なお、JavaScriptであれば以下のような書き方で解決できます。ただし煩雑かつ過剰に技巧的で驚きを最小にできておらず、現実的ではないように思います。オブジェクトに振る舞いを持たせることになり、それだったらクラスを定義すべきである気がします。
// "age"プロパティを持つオブジェクトにアタッチして利用する関数
// thisの挙動に依存するのでアロー関数では正しく動作しない
function isAdult(){
return this["age"] >= 18
}
// オブジェクト生成時にisAdultもアタッチしておく
const taro = {"name": "Taro", "age": 18, "isAdult": isAdult}
const jiro = {"name": "Jiro", "age": 17, "isAdult": isAdult}
if (taro.isAdult()){ // 一応解決している
console.log(`${taro.name}は成人です`)
} else {
console.log(`${taro.name}は未成年です`)
}
if (jiro.isAdult()){ // 一応解決している
console.log(`${jiro.name}は成人です`)
} else {
console.log(`${jiro.name}は未成年です`)
}