4

How can I / should I perform reverse operators in Ruby? For example, I have implemented the operator '+' for my object A so now A + 2 works well. Problem is: 2 + A does not work :( Python provides __radd__ to address this issue. How should I attack this issue with Ruby?

Do I have to override the '+' operator of Integer? How about if it is a float, do I have to override the '+' operator from Float too? I like Ruby more than Python (strictly a personal and subjective opinion) but in Python you can simply do:

def __radd__(self, other): # other + self
    return self + other

Python provides __add__ and __radd__ that can be overridden. Does Ruby provide something similar?

Hoping that there is a quick and easy solution for that in Ruby, as 99.9% of the times there is.

Thanks!

  • 2
    It might look at first glance like the question is different but the accepted answer is exactly what you're looking to do. You won't be able to do `2+a` because [Numerics in Ruby are immutable](https://stackoverflow.com/a/4235308/3784008) and [the `+` method](https://docs.ruby-lang.org/en/master/Integer.html#method-i-2B) doesn't mutate the receiver and only accepts another Numeric to return a new Numeric that is the sum of the previous two. – anothermh Mar 17 '23 at 16:28
  • You really need to include a reproducible example in your question body. How for example did you implement operator +. Can you show the code defining that? Also, can you show us how A+2 works in a quick reproducible code snippet? How about an example of the results or error message you are seeing when you try 2+A. Without these examples we're not only forced to do a bunch of extra work, be we also have to make a lot of assumptions. In addition, we don;t have a good reference point around which to formulate a good and accurate response. –  Mar 17 '23 at 17:51
  • By the way, it might also help if you were to tag the question with ```Python``` as well. You're describing a method or function of some sort in Python that many Rubyists may have no understanding of at all and you're using it as a core part of your question. –  Mar 17 '23 at 18:05
  • For anyone that comes later, the question and answer I was referencing are https://stackoverflow.com/a/43383717/3784008 – anothermh Mar 17 '23 at 21:57

1 Answers1

2

In Python __radd__, __rsub__, and friends are used as follows: Docs

These methods are called to implement the binary arithmetic operations (+, -, *, @, /, //, %, divmod(), pow(), **, <<, >>, &, ^, |) with reflected (swapped) operands. These functions are only called if the left operand does not support the corresponding operation and the operands are of different types. For instance, to evaluate the expression x - y, where y is an instance of a class that has an __rsub__() method, type(y).__rsub__(y, x) is called if type(x).__sub__(x, y) returns NotImplemented.

Ruby does not have something exactly like this (nor this explicit); however ruby can perform a similar process using #coerce to ensure interoperability with instances of other numeric classes.

Let's assume A looks like this

class A 
  attr_reader :attr
  def initialize(a) 
    @attr = a 
  end 
 
  def +(other)
    other = other.attr if other.is_a?(A)
    A.new(attr + other)
  end 
end

Usage:

a = A.new(12)
a + 2 
#=> #<A:0x00007fb2942c7f38 @attr=14>
a + a 
#=> #<A:0x00007fb294156eb0 @attr=24>
2 + a 
#=> `+': A can't be coerced into Integer (TypeError)

In this case you want to be able to use Integer#+ with A as an argument. To do this you need to define A#coerce

class A 
  def coerce(other) 
    [A.new(other), self]
  end 
end 
a = A.new(10) 
2 + a 
#=> #<A:0x00007fb2942e55d8 @attr=12>

If you would rather 2 + a return an Integer you can change the Array provided by coerce

def coerce(other) 
  [other,attr] 
end

a = A.new(5) 
2 + a 
#=> 7  

What happens here (in simplified terms) is Integer#+ will see if it can add itself with the instance of A. Since it does not understand what an A is it will then call the coerce method and try the same message again with the return values.

It pseudo works like this

arg1 = 2 
arg2 = A.new(12) 
message = :+ 
begin
  arg1.public_send(message,arg2) 
rescue TypeError 
  arg1, arg2 = arg2.coerce(arg1)
  retry
end 
engineersmnky
  • 25,495
  • 2
  • 36
  • 52
  • I don't think you should have posted an answer to this question. There's not a reproducible example and you're making a lot of assumptions. –  Mar 17 '23 at 17:47
  • No. I'm actually not making an assumption that it doesn't answer the question. I'm actually assuming it does. What you're doing though is encouraging poor quality questions. You rewarded the OP with what they needed. What motivation do they now have to properly word the question in such a way that its a high quality question that will be useful for future users? There really isn't any. –  Mar 17 '23 at 18:53
  • Additionally (and I have a lot of respect for [Aleksei Matiushkin](https://stackoverflow.com/questions/43383145/ruby-overload-operator-behaviour-for-some-cases-only)) I abhor the implementation and the modification of a integral part of a core class proposed in the selected answer of the [duplicate](https://stackoverflow.com/questions/43383145/ruby-overload-operator-behaviour-for-some-cases-only) and disagree this closure as `coerce` is far closer to [`__radd__`](https://docs.python.org/3/reference/datamodel.html?highlight=__radd__#object.__radd__) – engineersmnky Mar 17 '23 at 19:09
  • I assume you're not too keen on the minimal reproducible example then either? By the way, it could also be noted that the question references both "Object A" and "Class A" which could mean any number of things as could "implemented the operator '+'". How that operator is "implemented" could mean any number of things. Regardless, my suggestion is simply that providing an answer whether good or not when Stack Overflow guidelines have CLEARLY not been met, doesn't really encourage proper participation...especially for new users. –  Mar 17 '23 at 19:12
  • @MichaelB Again thank you for your opinion. I'll take it under consideration. – engineersmnky Mar 17 '23 at 19:37
  • Well, I think it's a good answer. – anothermh Mar 17 '23 at 21:56