Conditional overriding of operators

Hello!

I am trying to replace .+ operator of Numeric class if and only if
argument (summand) is my own class.

To be more specific: I am writing geometry module in Ruby and need to
implement vector-number multiplication so that both:

2 * vector
vector * 2

work well.

Therefore I need to replace .+ method of Numeric without spoiling
arithmetical operations with other classes. It is clear, that in case of
inheritance we can just write:

class MyNumeric < Numeric
  def +(object)
    super(object) unless object.kind_of?(Vector)

    # vector-number multiplication here...
  end
end

But, of course, we need a replacement here. How to implement it?

class Numeric
  def +(vector)
    unless vector.kinf_of?(Vector)
      # What to write here??!
    end

    # vector-number multiplication here...
end

···

--
Posted via http://www.ruby-forum.com/.

class Numeric
    alias_method :_plus, :+
   def +(vector)
   unless vector.kind_of?(Vector)
   return _plus(vector)
   end

   # vector-number multiplication here...
  end

But maybe better something like:

  class Numeric
    def self.op_map(op, klass=nil, &block)
      op = op.to_sym
      @op_map ||= {}
      @op_map[op] ||= {}
      return @op_map[op] unless klass
      return @op_map[op][klass] unless block
      @op_map[op][klass] = block
    end

    alias_method :_plus, :+

    def +(operand)
      if procedure = self.class.op_map[:+][operand.class]
        procedure.call(operand)
      else
        _plus(vector)
      end
    end
  end

  Numeric.op_map(:+,Vector) do |operand|
    # ... your vector addition here
  end

T.

···

On Nov 4, 4:52 am, Daniel Vartanov <daniel.varta...@gmail.com> wrote:

Hello!

I am trying to replace .+ operator of Numeric class if and only if
argument (summand) is my own class.

To be more specific: I am writing geometry module in Ruby and need to
implement vector-number multiplication so that both:

2 * vector
vector * 2

work well.

Therefore I need to replace .+ method of Numeric without spoiling
arithmetical operations with other classes. It is clear, that in case of
inheritance we can just write:

class MyNumeric < Numeric
def +(object)
super(object) unless object.kind_of?(Vector)

\# vector\-number multiplication here\.\.\.

end
end

But, of course, we need a replacement here. How to implement it?

Daniel, look at [ruby-talk:98763], for example here:

  http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/98763

Regards,
Pit

···

2008/11/4 Daniel Vartanov <daniel.vartanov@gmail.com>:

I am trying to replace .+ operator of Numeric class if and only if
argument (summand) is my own class.

To be more specific: I am writing geometry module in Ruby and need to
implement vector-number multiplication so that both:

2 * vector
vector * 2

work well.

No, you don't. You need to implement #coerce and #+, #- etc. in
*your* class properly (see the link Pit provided for one, albeit not
very complete example). Basically your coerce is invoked when Fixnum#+
sees an instance of your class so you are in control what operation is
finally implemented.

You can find more hits with
http://blade.nagaokaut.ac.jp/cgi-bin/vframe.rb?key=coerce&cginame=namazu.rb&submit=Search&dbname=ruby-talk&max=50&whence=0

Cheers

robert

···

2008/11/4 Daniel Vartanov <daniel.vartanov@gmail.com>:

I am trying to replace .+ operator of Numeric class if and only if
argument (summand) is my own class.

To be more specific: I am writing geometry module in Ruby and need to
implement vector-number multiplication so that both:

2 * vector
vector * 2

work well.

Therefore I need to replace .+ method of Numeric without spoiling
arithmetical operations with other classes.

--
remember.guy do |as, often| as.you_can - without end

Daniel Vartanov ha scritto:

Hello!

I am trying to replace .+ operator of Numeric class if and only if
argument (summand) is my own class.

To be more specific: I am writing geometry module in Ruby and need to
implement vector-number multiplication so that both:

2 * vector
vector * 2

work well.

Therefore I need to replace .+ method of Numeric without spoiling
arithmetical operations with other classes.
  
Hi Daniel,

take a look at:

http://www.splatbang.com/rubyquiz/quiz.rhtml?id=179_Modular_Arithmetic

Hope that helps.
Andrea

Thanks, guys! coercing mechanism is really useful. Due to multiplication
is commutative, I just swapped multiplier in coerce method, so even if
(2 * vector) is called, it calls vector.+(2) finally.

def *(scalar)
  Vector.new(x * scalar, y * scalar)
end

def coerce(scalar)
  [self, scalar]
end

vector * 2 # vector.*(2) is called
2 * vector # 2.*(vector), then vector.coerce(2), then vector.*(2)

Thanks again! :slight_smile:

···

--
Posted via http://www.ruby-forum.com/.

Thanks, guys! coercing mechanism is really useful. Due to multiplication
is commutative, I just swapped multiplier in coerce method, so even if
(2 * vector) is called, it calls vector.+(2) finally.

def *(scalar)
Vector.new(x * scalar, y * scalar)
end

def coerce(scalar)
[self, scalar]
end

I am not sure whether this implementation adheres to the contract of
#coerce. Normally you need to return the representative of the
*other* instance first:

irb(main):002:0> 1.coerce 2.0
=> [2.0, 1.0]

It may be ok in your case though. Note also, that you do no type
checking in #* which you should do in order to properly react on
values other than scalars. IMHO normally the logic should be "if the
other instance is not of the same class as self invoke #coerce on it".

vector * 2 # vector.*(2) is called
2 * vector # 2.*(vector), then vector.coerce(2), then vector.*(2)

Kind regards

robert

···

2008/11/4 Daniel Vartanov <daniel.vartanov@gmail.com>:

--
remember.guy do |as, often| as.you_can - without end

Hi, Robert.

I am not sure whether this implementation adheres to the contract of
#coerce. Normally you need to return the representative of the
*other* instance first:
irb(main):002:0> 1.coerce 2.0
=> [2.0, 1.0]

Swapping is the main point here, we exploit commutativity of
multiplication. If Numeric can be *converted back* to your type, you can
use this approach (actually you mentioned it in one of threads ;)):
def coerce(x) [self.class.new( x ), self]; end

But Numeric cannot be converted back to Vector. Just look what people do
in case of subtraction, which is not commutative:
http://groups.google.com/group/ruby-talk-google/browse_thread/thread/aa288902532dcc6c
(note, he uses swapping too)

Note also, that you do no type checking in #* which you should do in order to
properly react on values other than scalars.

I think you a right here, thanks for comment. I relied on the Vector.*
method, where an exception will be raised if numerical components of
vector is multiplied by something else than Numeric. But such exception
is not descriptive enough, so I decided to include such checking:

    def coerce(scalar)
      if scalar.is_a?(Numeric)
        [self, scalar]
      else
        raise ArgumentError, "Vector: cannot coerce #{scalar.inspect}"
      end
    end

···

--
Posted via http://www.ruby-forum.com/\.