Re-opening an existing module and changing a method

A

Aldric Giacomoni

"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:=> sqrt(2)
Actual result, mileage does not vary:=> 1.4142135623731

require 'mathn'
module Math
alias :eek:ld_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?
 
M

Marnen Laibow-Koser

Aldric said:
"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:
=> sqrt(2)
Actual result, mileage does not vary:
=> 1.4142135623731

require 'mathn'
module Math
alias :eek:ld_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,
 
J

Jesús Gabriel y Galán

"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:
=3D> sqrt(2)
Actual result, mileage does not vary:
=3D> 1.4142135623731

require 'mathn'
module Math
=A0alias :eek:ld_sqrt :sqrt
=A0def sqrt x
=A0 =A0result =3D old_sqrt x
=A0 =A0if result.is_a? Float
=A0 =A0 =A0"sqrt(#{x})"
=A0 =A0else
=A0 =A0 =A0result
=A0 =A0end
=A0end
end

puts Math.sqrt(2)

I had originally not attempted an alias, I just did "result =3D 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 =3D 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
=3D> 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.
 
J

Jesús Gabriel y Galán

2009/11/19 Jes=FAs Gabriel y Gal=E1n said:
"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:
=3D> sqrt(2)
Actual result, mileage does not vary:
=3D> 1.4142135623731

require 'mathn'
module Math
=A0alias :eek:ld_sqrt :sqrt
=A0def sqrt x
=A0 =A0result =3D old_sqrt x
=A0 =A0if result.is_a? Float
=A0 =A0 =A0"sqrt(#{x})"
=A0 =A0else
=A0 =A0 =A0result
=A0 =A0end
=A0end
end

puts Math.sqrt(2)

I had originally not attempted an alias, I just did "result =3D 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 =3D 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
=3D> Math
irb(main):050:0> Math.sqrt 2
NoMethodError: super: no superclass method `sqrt'
=A0 =A0 =A0 =A0from (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.

Got it:

irb(main):001:0> require 'mathn'
=3D> true
irb(main):002:0> module Math
irb(main):003:1> class << self
irb(main):004:2> alias :eek:ld_sqrt :sqrt
irb(main):005:2> end
irb(main):006:1> def sqrt x
irb(main):007:2> result =3D 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
=3D> Math
irb(main):013:0> Math.sqrt 2
[1.4142135623731, Float]
=3D> 1.4142135623731

Jesus.
 
A

Aldric Giacomoni

Marnen said:
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? :) I, er, forgot to do self.sqrt ...

module Math
alias_method :eek:ld_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"}
 
A

Aldric Giacomoni

Jesús Gabriel y Galán said:
irb(main):003:1> class << self
irb(main):004:2> alias :eek:ld_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 :eek:ld_sqrt :sqrt
end

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

=> sqrt(2)
 
J

Jesús Gabriel y Galán

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

=A0class << self
=A0 =A0alias :eek:ld_sqrt :sqrt
=A0end

=A0def self.sqrt x
=A0 =A0result =3D old_sqrt(x)
=A0 =A0if result.is_a? Float
=A0 =A0 =A0"sqrt(#{x})"
=A0 =A0else
=A0 =A0 =A0result
=A0 =A0end
=A0end
end


=3D> sqrt(2)

Jesus.
 
A

Aldric Giacomoni

Julian said:
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 ?
 
J

Jesús Gabriel y Galán

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.
 
A

Aldric Giacomoni

Jesús Gabriel y Galán said:
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? :)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,774
Messages
2,569,599
Members
45,163
Latest member
Sasha15427
Top