RstructuralにOptionとEitherを追加した
前にRstructuralというGemを作ったという記事を書いた。
それの続編としてOption
とEither
を実装した、というだけの話。
Option
Scalaやってる人にはOption
、HaskellとかならMaybe
で伝わるはず。
「要素があるかも知れないし無いかも知れない」を表現するための型で、Rubyだとすぐにnil
が出てきてあらゆるオブジェクトがnil
の可能性もあり大変なのでもう少し安全に扱いたいなということで実装した。
しかしそうなるとすべての要素をOption
にする必要があり大変だが、名前の通りオプショナルな値をOption
で包んでおくと便利に扱えるのではないかなと。
Option.of
がファクトリになっていて、map
とかflat_map
が生えている。
x = Option.of(100).flat_map do |v| Option.of(v * 2) end #=> Option::Some(value: 200) x.get_or_else { 100 } #=> 200
get_or_else
の引数はデフォルト値でも良いけどブロックを渡せるようにもなっていてOption::Some
の時には無駄に評価されないようになっていたりして便利。
Scalaのcall-by-nameの仕組みは便利だったなほんと。
とはいえnil
を安全に扱うための言語的なサポートが無いので、Option
があっても気を付けないと何も変わらないので面倒だしあんまり使わない気もする。
Either
値がLeftかRightのどちらかであることを表現する型。
Rubyだと失敗がnil
か例外になることが多いような気がしていてもう少し明示的に扱いたいと思ったので実装した。
Either.try(&block)
がファクトリになっていて例外が起きたらEither::Left
になる。
Either.try { 100 } #=> Either::Right(value: 100) Either.try { raise "this is error" } #=> Either::Left(value: this is error)
明示的にLeftかRightかを指定してnewしたければEither.left
とEither.right
が生えている。
Either.right(100) .flat_map { |v| Either.left(v * 2) } #=> Either::Left(value: 200) .right_or_else { 0 } #=> 0 Either.left(100) .map_left { |v| v * 2 } #=> Either::Left(value: 200) .left_or_else { 0 } #=> 200
やはりmap
とflat_map
が生えていて、さらにright-biasedになっていて便利!
さらにパターンマッチと組み合わせるとこういう雰囲気
case Either.try { do_something() } in Either::Right[value] value * 2 in Either::Left[error] logger.error("error: #{error}") 0 end
結果の成功失敗をラップする型があるとわりと便利かなと思うこともあり、Option
よりは使い勝手良さそうな気もする。
感想
こういうのがあるとなるべくifとかtryを使わずにパターンマッチだけで実装できて気持ちが良い。