やわらかテック

興味のあること。業務を通して得られた発見。個人的に試してみたことをアウトプットしています🍵

【Ruby】boolean型の値を渡すだけの関数にはキーワード引数を付与してあげよう

ある日のこと。Rubyのコードを読んでいるとboolean型の値が引数となる関数の呼び出し処理を見て「なんじゃこりゃ...」という気持ちになりました。簡単な例を紹介します。

def say_hello(is_morning)
  if is_morning
    puts 'おはようございます'
  else
    puts 'こんにちは'
  end
end

say_hello(true) # おはようございます
say_hello(false) # こんにちは

引数のis_morningによって朝であれば「おはようございます」を出力して、朝でなければ「こんにちは」と出力します。

シンプルな関数で挙動は明確ですが、問題なのは呼び出し側です。
呼び出し時にtruefalseを指定することになりますが、関数の詳細を知らなければ呼び出し側で適切な値を選択することができません。同様の理由で、呼び出し側のコードを見た時にtruefalseが何を指すのかが全く分かりません。

say_hello(true) # trueが意味するものとは...?

※Rubyには型がないのでboolean型以外も考慮が必要なんじゃ...と思いますが、今回は省略します。

対策: キーワード引数を使う

Ruby2.x系以上であれば、キーワード引数を使うことができます。先ほどのsay_helloをキーワード引数を使って定義し直してみます。

def say_hello(is_morning:)
  if is_morning
    puts 'おはようございます'
  else
    puts 'こんにちは'
  end
end

この状態で、先ほどの呼び出しをするとエラーになります。「is_morningたるキーワード引数が定義されているのに、指定されていないですよ?」という旨の内容です。

say_hello(true)
# `say_hello': wrong number of arguments (given 1, expected 0; required keyword: is_morning) (ArgumentError)

呼び出し側もキーワード引数に対応させます。
どうでしょうか。なぜtrueなのか(朝なのかどうか)が、より明確になったと思います。

say_hello(is_morning: true) # おはようございます
say_hello(is_morning: false) # こんにちは

最後にis_morningtruefalseによって挙動がどのように変わるのかが推測しにくいので、関数名を改めます。

def say_good_morning_or_hello(is_morning:)
  :
end

say_good_morning_or_hello(is_morning: true)

良い感じになりました。

最後に

「こんな実装しないでしょ」と思われるかもしれませんが、自分の経験上、割とやってしまいがちな実装だと思います。やっかいなのは、実装が手軽な上に実装者は違和感なく使えるのに対して、他のメンバーからすると「何、この関数...」となる点です。
今回は引数が1つのケースを紹介しましたが、過去に自分はboolean型の値をいくつも受け取るとんでもない関数を定義してしまったことがあります。

do_something(true, true, false)

何が何だか全くわかりません。キーワード引数を使えばマシにはなると思いますが、そもそも関数の設計が良いかどうかを疑う必要がありそうです。

少しでも「ええな〜」と思ったらイイネ!・シェア!・はてなブックマークを頂けると励みになります。