Re-opening an existing module and changing a method

"I hear and I forget; I see and I remember; I do and I understand."
Wouldn't it be nice if it were that simple - clearly they did not know
about the subtle art of debugging.

So, I'm trying to hack at the math module.
Expected:

Math.sqrt(2)

=> sqrt(2)
Actual result, mileage does not vary:

Math.sqrt(2)

=> 1.4142135623731

require 'mathn'
module Math
  alias :old_sqrt :sqrt
  def sqrt x
    result = old_sqrt x
    if result.is_a? Float
      "sqrt(#{x})"
    else
      result
    end
  end
end

puts Math.sqrt(2)

I had originally not attempted an alias, I just did "result = super x"
but it didn't really amount to much, either.
Where am I thinking about this wrong?

···

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

Aldric Giacomoni wrote:

"I hear and I forget; I see and I remember; I do and I understand."
Wouldn't it be nice if it were that simple - clearly they did not know
about the subtle art of debugging.

So, I'm trying to hack at the math module.
Expected:

Math.sqrt(2)

=> sqrt(2)
Actual result, mileage does not vary:

Math.sqrt(2)

=> 1.4142135623731

require 'mathn'
module Math
  alias :old_sqrt :sqrt
  def sqrt x
    result = old_sqrt x
    if result.is_a? Float
      "sqrt(#{x})"
    else
      result
    end
  end
end

puts Math.sqrt(2)

I had originally not attempted an alias, I just did "result = super x"
but it didn't really amount to much, either.
Where am I thinking about this wrong?

Looks fine to me. Does old_sqrt actually return a Float or some other
numeric type?

Actually, why use type checking at all? Why not change the condition to
use kind_of or simply test result.to_i - result == 0 ?

Best,

···

--
Marnen Laibow-Koser
http://www.marnen.org
marnen@marnen.org
--
Posted via http://www.ruby-forum.com/\.

The problem is that mathn is using the module_function method to
convert sqrt into an method of the Math module.
According to http://ruby-doc.org/core/classes/Module.src/M001642.html:

"Module functions are copies of the original, and so may be changed
independently"

So I think you are not redefining the module function, but the
original, which doesn't have any effect when you call Math.sqrt (this
is calling the version created by module_function). Doing this:

irb(main):042:0> module Math
irb(main):043:1> def sqrt x
irb(main):044:2> result = super x
irb(main):045:2> p [result, result.class]
irb(main):046:2> result
irb(main):047:2> end
irb(main):048:1> module_function :sqrt
irb(main):049:1> end
=> Math
irb(main):050:0> Math.sqrt 2
NoMethodError: super: no superclass method `sqrt'
  from (irb):44:in `sqr

allows you to actually override the version created by
module_function, but I don't know how to then call the original, since
neither the original alias you had nor super are working. But maybe
this points you in the right direction.

Jesus.

···

On Thu, Nov 19, 2009 at 6:27 PM, Aldric Giacomoni <aldric@trevoke.net> wrote:

"I hear and I forget; I see and I remember; I do and I understand."
Wouldn't it be nice if it were that simple - clearly they did not know
about the subtle art of debugging.

So, I'm trying to hack at the math module.
Expected:

Math.sqrt(2)

=> sqrt(2)
Actual result, mileage does not vary:

Math.sqrt(2)

=> 1.4142135623731

require 'mathn'
module Math
alias :old_sqrt :sqrt
def sqrt x
result = old_sqrt x
if result.is_a? Float
"sqrt(#{x})"
else
result
end
end
end

puts Math.sqrt(2)

I had originally not attempted an alias, I just did "result = super x"
but it didn't really amount to much, either.
Where am I thinking about this wrong?

You're doing instance methods when you really want class methods.

Good time to google the difference if you don't already know.

Julian

Blog: http://random8.zenunit.com/
Twitter: http://twitter.com/random8r
Learn: http://sensei.zenunit.com/
New video up now at http://sensei.zenunit.com/ real fastcgi rails deploy process! Check it out now!

···

On 20/11/2009, at 4:27 AM, Aldric Giacomoni <aldric@trevoke.net> wrote:

"I hear and I forget; I see and I remember; I do and I understand."
Wouldn't it be nice if it were that simple - clearly they did not know
about the subtle art of debugging.

So, I'm trying to hack at the math module.
Expected:

Math.sqrt(2)

=> sqrt(2)
Actual result, mileage does not vary:

Math.sqrt(2)

=> 1.4142135623731

require 'mathn'
module Math
alias :old_sqrt :sqrt
def sqrt x
   result = old_sqrt x
   if result.is_a? Float
     "sqrt(#{x})"
   else
     result
   end
end
end

puts Math.sqrt(2)

I had originally not attempted an alias, I just did "result = super x"
but it didn't really amount to much, either.
Where am I thinking about this wrong?
--
Posted via http://www.ruby-forum.com/\.

Got it:

irb(main):001:0> require 'mathn'
=> true
irb(main):002:0> module Math
irb(main):003:1> class << self
irb(main):004:2> alias :old_sqrt :sqrt
irb(main):005:2> end
irb(main):006:1> def sqrt x
irb(main):007:2> result = old_sqrt x
irb(main):008:2> p [result, result.class]
irb(main):009:2> result
irb(main):010:2> end
irb(main):011:1> module_function :sqrt
irb(main):012:1> end
=> Math
irb(main):013:0> Math.sqrt 2
[1.4142135623731, Float]
=> 1.4142135623731

Jesus.

···

2009/11/19 Jesús Gabriel y Galán <jgabrielygalan@gmail.com>:

On Thu, Nov 19, 2009 at 6:27 PM, Aldric Giacomoni <aldric@trevoke.net> wrote:

"I hear and I forget; I see and I remember; I do and I understand."
Wouldn't it be nice if it were that simple - clearly they did not know
about the subtle art of debugging.

So, I'm trying to hack at the math module.
Expected:

Math.sqrt(2)

=> sqrt(2)
Actual result, mileage does not vary:

Math.sqrt(2)

=> 1.4142135623731

require 'mathn'
module Math
alias :old_sqrt :sqrt
def sqrt x
result = old_sqrt x
if result.is_a? Float
"sqrt(#{x})"
else
result
end
end
end

puts Math.sqrt(2)

I had originally not attempted an alias, I just did "result = super x"
but it didn't really amount to much, either.
Where am I thinking about this wrong?

The problem is that mathn is using the module_function method to
convert sqrt into an method of the Math module.
According to http://ruby-doc.org/core/classes/Module.src/M001642.html:

"Module functions are copies of the original, and so may be changed
independently"

So I think you are not redefining the module function, but the
original, which doesn't have any effect when you call Math.sqrt (this
is calling the version created by module_function). Doing this:

irb(main):042:0> module Math
irb(main):043:1> def sqrt x
irb(main):044:2> result = super x
irb(main):045:2> p [result, result.class]
irb(main):046:2> result
irb(main):047:2> end
irb(main):048:1> module_function :sqrt
irb(main):049:1> end
=> Math
irb(main):050:0> Math.sqrt 2
NoMethodError: super: no superclass method `sqrt'
from (irb):44:in `sqr

allows you to actually override the version created by
module_function, but I don't know how to then call the original, since
neither the original alias you had nor super are working. But maybe
this points you in the right direction.

Marnen Laibow-Koser wrote:

Looks fine to me. Does old_sqrt actually return a Float or some other
numeric type?

Actually, why use type checking at all? Why not change the condition to
use kind_of or simply test result.to_i - result == 0 ?

Well.. Not that it's particularly meaningful, but here's my benchmark
for that question:

require 'benchamrk'
num = 5.5
n = 5_000_000
Benchmark.bmbm do |x|
  x.report("kind_of?") { n.times do ; n.kind_of? Float ; end }
  x.report("is_a?") { n.times do ; n.is_a? Float ; end }
  x.report("to_i") { n.times do ; n.to_i - n == 0 ; end }
end

Rehearsal --------------------------------------------
kind_of? 1.719000 0.000000 1.719000 ( 1.720000)
is_a? 1.641000 0.000000 1.641000 ( 1.642000)
to_i 2.750000 0.000000 2.750000 ( 2.751000)
----------------------------------- total: 6.110000sec

               user system total real
kind_of? 1.734000 0.000000 1.734000 ( 1.735000)
is_a? 1.703000 0.000000 1.703000 ( 1.720000)
to_i 2.688000 0.000000 2.688000 ( 2.689000)

···

__________________

You know how metaprogramming is all about the self, according to Yehuda
Katz's latest blog post? :slight_smile: I, er, forgot to do self.sqrt ...

module Math
  alias_method :old_sqrt, :sqrt
  def self.sqrt x
    result = 5.5 #self.old_sqrt(x)
    if result.is_a? Float
      "sqrt(#{x})"
    else
      result
    end
  end
end

puts Math.sqrt(2)

=> sqrt(2)

puts Math.methods.find { |i| i[0..0] == "o"}

=> object_id

... So I'm not sure how to do an alias_method on a method that's got a
"self." in front of it, I guess..
puts Math.methods.find { |i| i[0..0] == "o"}
--
Posted via http://www.ruby-forum.com/\.

Julian Leviston wrote:

You're doing instance methods when you really want class methods.

Good time to google the difference if you don't already know.

Is this a new insight, or is this just putting words to what Jesus
already helped me discover (and hack) ?
Instance method : Customer.new.phone # (probably yields an error : phone
number not set)
Class method : Customer.find(:first) # (Rails-style)

Right ?

···

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

Jesús Gabriel y Galán wrote:

irb(main):003:1> class << self
irb(main):004:2> alias :old_sqrt :sqrt
irb(main):005:2> end

Ah-ha! My version is underneath. So, what the bit of code I quoted does
is.. It reopens "the class of the module" to make a change in the alias?

module Math

  class << self
    alias :old_sqrt :sqrt
  end

  def self.sqrt x
    result = old_sqrt(x)
    if result.is_a? Float
      "sqrt(#{x})"
    else
      result
    end
  end
end

puts Math.sqrt(2)

=> sqrt(2)

···

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

Julian Leviston wrote:

You're doing instance methods when you really want class methods.

Good time to google the difference if you don't already know.

Is this a new insight, or is this just putting words to what Jesus
already helped me discover (and hack) ?

Well, people usually call class methods to methods that are defined in
the singleton class of a class.
In this case it could be a bit confusing, since we are not talking
about a class but a Module.
So I would call it a module method (or module function).

Instance method : Customer.new.phone # (probably yields an error : phone
number not set)
Class method : Customer.find(:first) # (Rails-style)

Right ?

Right, but it would be more clear to say module method, in my opinion.

Jesus.

···

On Fri, Nov 20, 2009 at 1:47 PM, Aldric Giacomoni <aldric@trevoke.net> wrote:

Jesús Gabriel y Galán wrote:

irb(main):003:1> class << self
irb(main):004:2> alias :old_sqrt :sqrt
irb(main):005:2> end

Ah-ha! My version is underneath. So, what the bit of code I quoted does
is.. It reopens "the class of the module" to make a change in the alias?

Yes, module_function creates a method in the singleton class of the module.
So you need to enter that singleton class via class << self to be in a
place where self is that singleton class, in order to be able to alias
that method correctly.

module Math

class << self
alias :old_sqrt :sqrt
end

def self.sqrt x
result = old_sqrt(x)
if result.is_a? Float
"sqrt(#{x})"
else
result
end
end
end

puts Math.sqrt(2)

=> sqrt(2)

Jesus.

···

On Thu, Nov 19, 2009 at 7:45 PM, Aldric Giacomoni <aldric@trevoke.net> wrote:

Jesús Gabriel y Galán wrote:

Instance method : Customer.new.phone # (probably yields an error : phone
number not set)
Class method : Customer.find(:first) # (Rails-style)

Right ?

Right, but it would be more clear to say module method, in my opinion.

Well --

Math.sqrt(5) : Module method, because it's defined in a module
Customer.find(:first) : Class method, because it's defined in the class

Right? :slight_smile:

···

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