🙄
RubyでOptional型っぽいことをする
一部の言語では、Optional型(ないしOption型やMaybe型)を提供しています。
Optional型は「NULLかもしれない値」を安全に扱う型です。
RubyにはOptional型はありませんが、
Optional型のメリット
Optional型が導入される以前は、NULLチェックするのはプログラマーの責任でした、
Ticket ticket = getTicket(); // NULLを返すかもしれない関数
ticket.getTitle(); // 危険なコード。ticketがNULLだったら例外が発生するかもしれない
if (ticket != null) { // NULL かもしれないのでチェックする
ticket.getTitle(); // NULL ではないので確実にメソッド呼び出しできる
}
Optional型で値をラップすると、以降の処理を安全に書くことができます。
Optional<Ticket> maybeTicket = Optional.ofNullable(getTicket()); // NULLかもしれない値をラップ
// maybeTicket.getTitle(); // メソッドを緑説呼び出すことはできない
// 値がNULLでなければ getTitleメソッドを呼び、値をOptionalでラップして返す
Optional<String> maybeTitle = nullableString.map(ticket -> ticket.getTitle())
// 値がNULLでなければその値を、NULLなら "no title" を返す
String title = maybeTitle.orElse("no title");
RubyにはOptional型は無いけども
RubyにはOptional型が無く、裸のnilを使わなければなりません。
しかし、RubyはJavaなどと異なりnilに対してもメソッドを呼び出せます。そのためOptional型のようなこと(if文でnilチェックをせずに処理すること)ができます。
RubyでOptional型っぽいことをする例
値があれば取得し、無ければデフォルト値を使う(orElse、orElseGet)
title = maybeTitle || "no title"
title = maybeTitle || getDefaultTitle()
値があれば取得し、無ければ例外を送出する(orElseThrow)
title = maybeTitle or throw "title is missing"
# こっちの方がRuby的かも
throw "title is missing" if maybeTitle.nil?
title = maybeTitle
値があれば処理をする(ifPresent)
これはif
文を使わざるを得ないかも。そもそもJavaでもifPresent
は使う機会が少ない印象。
unless maybeTicket.nil?
process(maybeTicket)
end
値を変換する(map)
ぼっち演算子とthen
を組み合わせる。
maybeTitle = maybeBook&.then { |book| book.title }
条件を満たしていれば値を残す(filter)
値が条件を満たしていれば値を残し、条件を満たさなければ nil にする。
元々 nil であれば nil のまま。
maybeSaleBook = maybeBook if maybeBook&.then { |book| book.sale? }
# こう書く方がRuby的かも
maybeSaleBook = maybeBook if maybeBook&.sale?
配列に変換(stream)
Optionalを「要素数が0または1のコレクション」とみなし、配列に変換します。
Array
メソッドを使えば、大体のケースでこの変換ができるのですが、値がハッシュや配列など.to_ary や
.to_a`を定義したオブジェクトであると意図した結果になりません。
books = Array(maybeBook)
# Array(nil) => nil
# Array(42) => [42]
# Array("abc") => ["abc"]
# Array({:it => 3}) => [[:it, 3]]
# Array([1, 2, 3]) => [1, 2, 3]
# objの型に関わらず、配列に変換するにはこうする
maybeObj&.then { |obj| [obj] }.to_a
# こうする方がRuby的かも
arr = maybeBook ? [maybeBook]: []
感想
無理にOptionalっぽい書き方をするより、Ruby的な書き方をする方が良さそうです。
&.then
はイディオムとして覚えておくと、役立つこともありそうです。
Discussion
これはどうでしょうか?