Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

overloading an operator via a mixin

Tags:

ruby

Is there a way to override a class's operator, by creating a new operator method inside a module, then mixing that module into the class?

eg, this overrides Fixnum's + operator:

class Fixnum
  def +(x)
    product = x
    product = product * self
    return product
  end
end

p 3 + 3

# => 9

This does not override Fixnum's + operator:

module NewOperators
  def +(x)
    product = x
    product = product * self
    return product
  end  
end

class Fixnum
  include NewOperators
end

p 3 + 3

# => 6
like image 701
fengolly Avatar asked Jun 10 '11 01:06

fengolly


2 Answers

Your question led me to this interesting article which describes the problem:

Fixing Ruby’s Inheritance Model with Metamorph

This is the ultimate problem with Ruby’s inheritance model, in my opinion. Because mixins always take lower priority than methods defined directly in a class body, A#foo can not be overridden by mixin inclusion. Moreover, because mixins take lower priority than methods, A#foo is now violating encapsulation [...]

And his solution is to transparently define all new methods inside an anonymous inner module:

class Object
  def self.method_added(name)
    return if name == :initialize
    const_set(:InstanceMethods, Module.new) unless defined?(self::InstanceMethods)
    meth = instance_method(name)
    self::InstanceMethods.send(:define_method, name) {|*args, &block| meth.bind(self).call(*args, &block) }
    remove_method(name)
    include self::InstanceMethods
  end
end

This is also conveniently packaged in a library called metamorph, which allows you to have mixin methods override class methods by just requiring it.

like image 86
Kai Avatar answered Oct 04 '22 15:10

Kai


No, because in a method lookup you don't get a chance to pull in methods that were defined in mixins or parent classes until you've actually looked at the current class.

That said, you can create a method that, when called on a class, will create methods in that class. This can give you a way to inject operators into classes easily.

Be warned that actually doing this is a good recipe for surprise. This is doubly true when you are changing standard modules whose behavior is relied upon by others. See http://avdi.org/devblog/2008/02/23/why-monkeypatching-is-destroying-ruby/ for context.

like image 29
btilly Avatar answered Oct 04 '22 16:10

btilly