備忘録的な何か

技術ブログ的な何かです

Maybeモナド的な何かに惹かれて、RailsのtryのgetOrElse的なものを作った。

皆さん、tryっていいですよね。でも、MayBeモナド的なgetOrElseにつながるチェーンもかっこいいですよね。
ScalaでいうところのList(0,-1,1).find(_ > 1).getOrElse(999) // 999

ということで、以下を考えました。

class Object
  def try_else(*a,else_val, &b)
    if a.empty? #else値がない場合は、elseの場合はnilを返却
      a = [else_val]
      else_val = nil
    end

    if a.empty? || respond_to?(a.first)
      ret = if a.empty? && block_given?
              if b.arity.zero?
                instance_eval(&b)
              else
                yield self
              end
            else
              public_send(*a, &b)
            end
      ret.nil? ? else_val : ret #実行結果がnilの場合はelse値を返却
    end
  end
end

class NilClass
  def try_else(*args,else_val)
    args.empty? ? nil : else_val  #selfがnilの場合は、引数の最後を返却。必ず、nilを返したほうが良い?
  end
end

使い方は以下。チェーンが短くなってイイ!

#tryの場合
[0,-1,1].try(:find){|x| x > 1} #=> nil
[0,-1,3].try(:find){|x| x > 1} #=> 3
nil.try(:find){|x| x > 1} #=> nil

#tryのgetOrElse版の場合 (最後の引数をnilの場合に返却)
[0,-1,1].try_else(:find,999){|x| x > 1} #=> 999
[0,-1,3].try_else(:find,999){|x| x > 1} #=> 3
nil.try_else(:find,999){|x| x > 1} #=> 999

#Else値を入れないと、nilを返却。但し、引数がないメソッドの場合のみ。
[0,-1,1].try_else(:find){|x| x > 1} #=> nil
[0,-1,3].try_else(:find){|x| x > 1} #=> 3
nil.try_else(:find){|x| x > 1} #=> nil

#tryのgetOrElse版と同じことをtryでしようとする、結構だるい。
#tapからのbreakで値返しが一番楽そう。
[0,-1,1].try(:find){|x| x > 1}.tap{|x|break 999 if x.nil? } #=> 999