petitviolet_blog

@petitviolet blog

RstructuralにOptionとEitherを追加した

前にRstructuralというGemを作ったという記事を書いた。

それの続編としてOptionEitherを実装した、というだけの話。

Option

Scalaやってる人にはOptionHaskellとかなら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.leftEither.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

やはりmapflat_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を使わずにパターンマッチだけで実装できて気持ちが良い。