RubyのNumeric#coerce

最近Rubyのgemを作る中で作ったクラスを対象として四則演算をoverrideするものを作った時にはまったのでそれを書く。

何にはまったのか

例えば今回は加算を例にして下記のようなクラスを例に使う。

class Hoge
  def initialize(value:, weight: 10)
    @value = value
    @weight = weight
  end

  def +(other)
    @value + other + @weight
  end
end

動作としては下記のようになる。

> hoge = Hoge.new(value: 3)
> hoge + 5
# => 18

これは何も問題はない。
ただこれを 5 + hoge とすると下記のようになる。

> 5 + hoge
TypeError: Hoge can't be coerced into Integer

Hogeclassは計算相手じゃないと。

Numeric#coerce

この時使用するのがNumericクラスに生えているcoerceになる。
instance method Numeric#coerce (Ruby 2.4.0)

このメソッドをHogeclassで使用すればcoerceの引数には左辺の数値が入ってくれる。
これを使用して再度Hogeclassを再定義してあげる。

class Hoge
  def initialize(value:, weight: 10)
    @value = value
    @weight = weight
  end

  def +(other)
    @value + other + @weight
  end
 
  def coerce(other)
    [self, other]
  end
end

これでHogeクラスを対象として加算で左辺、右辺どちらに数値をおいても計算が出来るようになった。

> hoge = Hoge.new(value: 5)
> hoge + 5
# => 20
> 5 + hoge
# => 20

余談

これはPythonだともうちょっと楽にかける。

class Hoge:
    def __init__(self, value, weight=10):
        self.value = value
        self.weight = weight
    def add(self, other):
        return self.value + other + self.weight
    def __add__(self, other):
        return self.add(other)
    def __radd__(self, other):
        return self.add(other)
>>> hoge = Hoge(5)
>>> hoge + 5
20
>>> 5 + hoge
20