PHPの@演算子的なもの

http://d.hatena.ne.jp/gnarl/20080820/1219226223

例外かどうかを判断するのが誰であるべきかという話はおいといて、try-catch系の構文は、成功/失敗の二値が知りたいだけの場合でもあの長ったらしい構文を使わないといけないのがうざいなーとは常々思っていました。

Rubyだとこんな感じのブロックを取る演算があればいい?

class X
  @@exn = nil  # 直前に握り潰した例外

  # ブロックを評価し、例外が起きた場合は握り潰す
  def self.ignore_exception(recover_from=Exception)
    return nil if @@exn && !(@@exn.kind_of? recover_from)
    begin
      r = yield @@exn
      @@exn = nil
      r
    rescue => e
      @@exn = e
      nil
    end
  end
end

def _x(*a, &b)
  X.ignore_exception(*a, &b)
end

def parseint(s)
  if /^\d+$/ =~ s
    s.to_i
  else
    raise ArgumentError, "illegal format"
  end
end

parseint("123")       #=> 123
_x{parseint("123")}   #=> 123
parseint("hoge")      #=> 例外
_x{parseint("hoge")}  #=> nil が返る

_x{ ... } でくくると、中で例外が発生した場合は全て nil になります。(追記: これに近い働きをする rescue修飾子なんてものもありました。完全に忘れてました(^^;)

一般的には例外握り潰すとかマナー悪いもいいところですが、どんな例外でもひとつでも起きたら全部同じ処理っていうケースもよくあります。

if _x{ f(xxx) }
  // 成功したら...
else
  // 失敗したら...
end

特に長いメソッドチェーンでいちいちnilチェックしなくて済むのがうれしい。

result = _x{ tbl['xxx'][123][456] }
result = _x{ f(xxx).g(yyy).h(zzz) }

これはMaybeモナドっぽいと言えるかもしれない。

また ||演算子||-> 的に使えます(シングルスレッド限定)。

# 1番目で HogeError が起きた場合に限り, 2番目が実行される。
result = _x{...} ||_x(HogeError){|e|...} ||_x{...}

false や nil を途中で値として返したいときとか困りますがっ! (=> 追記:エラーモナド in Ruby)

たぶん計算をショートカットする二項演算というのが && や || の専売特許なのがあまり良くなくて、左辺のパターンマッチングでショートカットするかどうかを決められるような二項演算子定義機能があるとうれしい。