Defining a method vs aliasing it

L

leon breedt

Hi,

I'm not exactly sure why defining a method with "def methodname"
versus aliasing it would behave differently.

My example code:


---< snip >---

module O1
def self.append_features(base)
super
base.extend(ClassMethods)
base.class_eval do
class << self
alias_method :inherited_without_o1, :inherited
alias_method :inherited, :inherited_with_o1
end
end
puts "Including O1 into #{base.inspect}"
end
=20
module ClassMethods
def inherited_with_o1(child)
puts "O1: BEFORE WITHOUT"
inherited_without_o1(child)
puts "O1: AFTER WITHOUT"
puts "O1: #{child.inspect}"
end
end
end
=20
module O2
def self.append_features(base)
super
puts "Including O2 into #{base.inspect}"
base.class_eval do
class << self
alias_method :inherited_without_o2, :inherited
end
end
base.extend(ClassMethods)
end
=20
module ClassMethods
def inherited(child)
puts "O2: BEFORE WITHOUT"
inherited_without_o2(child)
puts "O2: AFTER WITHOUT"
puts "O2: #{child.inspect}"
end
end
end
=20
class Override
include O1
include O2
end
=20
class Child1 < Override
end
=20
class Child2 < Child1
end
=20
class Child3 < Override
end


---< snip >---

This prints:


Including O1 into Override
Including O2 into Override
O1: BEFORE WITHOUT
O1: AFTER WITHOUT
O1: Child1
O1: BEFORE WITHOUT
O1: AFTER WITHOUT
O1: Child2
O1: BEFORE WITHOUT
O1: AFTER WITHOUT
O1: Child3


If I instead modify O2 to use the same approach as O1, I get the
expected output:


Including O1 into Override
Including O2 into Override
O2: BEFORE WITHOUT
O1: BEFORE WITHOUT
O1: AFTER WITHOUT
O1: Child1
O2: AFTER WITHOUT
O2: Child1
O2: BEFORE WITHOUT
O1: BEFORE WITHOUT
O1: AFTER WITHOUT
O1: Child2
O2: AFTER WITHOUT
O2: Child2
O2: BEFORE WITHOUT
O1: BEFORE WITHOUT
O1: AFTER WITHOUT
O1: Child3
O2: AFTER WITHOUT
O2: Child3



Why would using an alias to create the new "inherited" work, but a
"def inherited" not?


Help much appreciated..
Leon
 
R

Robert Klemme

leon breedt said:
Hi,

I'm not exactly sure why defining a method with "def methodname"
versus aliasing it would behave differently.

My example code:


---< snip >---

module O1
def self.append_features(base)
super
base.extend(ClassMethods)
base.class_eval do
class << self
alias_method :inherited_without_o1, :inherited
alias_method :inherited, :inherited_with_o1
end
end
puts "Including O1 into #{base.inspect}"
end

module ClassMethods
def inherited_with_o1(child)
puts "O1: BEFORE WITHOUT"
inherited_without_o1(child)
puts "O1: AFTER WITHOUT"
puts "O1: #{child.inspect}"
end
end
end

module O2
def self.append_features(base)
super
puts "Including O2 into #{base.inspect}"
base.class_eval do
class << self
alias_method :inherited_without_o2, :inherited
end
end
base.extend(ClassMethods)
end

module ClassMethods
def inherited(child)
puts "O2: BEFORE WITHOUT"
inherited_without_o2(child)
puts "O2: AFTER WITHOUT"
puts "O2: #{child.inspect}"
end
end
end

class Override
include O1
include O2
end

class Child1 < Override
end

class Child2 < Child1
end

class Child3 < Override
end


---< snip >---

This prints:


Including O1 into Override
Including O2 into Override
O1: BEFORE WITHOUT
O1: AFTER WITHOUT
O1: Child1
O1: BEFORE WITHOUT
O1: AFTER WITHOUT
O1: Child2
O1: BEFORE WITHOUT
O1: AFTER WITHOUT
O1: Child3


If I instead modify O2 to use the same approach as O1, I get the
expected output:


Including O1 into Override
Including O2 into Override
O2: BEFORE WITHOUT
O1: BEFORE WITHOUT
O1: AFTER WITHOUT
O1: Child1
O2: AFTER WITHOUT
O2: Child1
O2: BEFORE WITHOUT
O1: BEFORE WITHOUT
O1: AFTER WITHOUT
O1: Child2
O2: AFTER WITHOUT
O2: Child2
O2: BEFORE WITHOUT
O1: BEFORE WITHOUT
O1: AFTER WITHOUT
O1: Child3
O2: AFTER WITHOUT
O2: Child3



Why would using an alias to create the new "inherited" work, but a
"def inherited" not?


Help much appreciated..
Leon

I guess it's this: the alias approach modifies method inherited of class
Override directly. But when you extend Override by O2, Override's inherited
shadows O2's inherited - so it's never called as there is no super call.

I'm not sure what exactly you want to achieve but maybe you are trying to do
more than necessary: if a class instance is extended by a module, sub class
instances inherit methods from the module automatically:
=> "bar"

Kind regards

robert
 
G

gwtmp01

I'm not exactly sure why defining a method with "def methodname"
versus aliasing it would behave differently.

It is my understanding that 'alias' doesn't behave in the sense that
it creates two method names that reference the same definition. Instead
it creates a new method and duplicates the definition of the old
message.
The resulting method is completely independent of the original method.

Maybe I'm missing something (probably) but there seems to be a semantic
mismatch between the word 'alias' and the behavior of the method
'alias'.

Shouldn't it be called dup_method or something similar?

P.S. I had similar confusion with 'include' because of my C background.
I just couldn't get it into my head that 'include' was not attempting
to read some file somewhere. Once I figured that out, the relationship
between 'require' and 'include' was much more clear.



Gary Wright
 
L

leon breedt

I guess it's this: the alias approach modifies method inherited of class
Override directly. But when you extend Override by O2, Override's inheri= ted
shadows O2's inherited - so it's never called as there is no super call.
Aha, this explanation, and Mauricio's, clears it up completely. Thanks!

Leon
 
D

David A. Black

Hi --

It is my understanding that 'alias' doesn't behave in the sense that
it creates two method names that reference the same definition. Instead
it creates a new method and duplicates the definition of the old message.
The resulting method is completely independent of the original method.

Maybe I'm missing something (probably) but there seems to be a semantic
mismatch between the word 'alias' and the behavior of the method 'alias'.

Shouldn't it be called dup_method or something similar?

I believe it's the other way around: aliasing a method name just adds
another name for the same method. If you then redefine the original
method, the two names then point to different methods:

class C
def x; end
alias :y :x # y and x are the same method
def x; "new method"; end # they are not the same
end

At least that's always been my understanding of it.


David
 
R

Ryan Leavengood

David said:
I believe it's the other way around: aliasing a method name just adds
another name for the same method. If you then redefine the original
method, the two names then point to different methods:

class C
def x; end
alias :y :x # y and x are the same method
def x; "new method"; end # they are not the same
end

At least that's always been my understanding of it.

I thought you were correct, and irb agrees:

irb(main):001:0> class C
irb(main):002:1> def a;puts 'a';end
irb(main):003:1> alias :b :a
irb(main):004:1> end
=> nil
irb(main):005:0> c = C.new
=> #<C:0x2b60238>
irb(main):006:0> c.a
a
=> nil
irb(main):007:0> c.b
a
=> nil
irb(main):008:0> ma = c.method:)a)
=> #<Method: C#a>
irb(main):009:0> mb = c.method:)b)
=> #<Method: C#b>
irb(main):010:0> ma.call
a
=> nil
irb(main):011:0> mb.call
a
=> nil
irb(main):012:0> ma == mb
=> true
irb(main):013:0> ms = c.method:)to_s)
=> #<Method: C(Kernel)#to_s>
irb(main):014:0> ms.call
=> "#<C:0x2b60238>"
irb(main):015:0> ms == ma
=> false
irb(main):016:0> class C
irb(main):017:1> def b;puts 'b';end
irb(main):018:1> end
=> nil
irb(main):019:0> c.b
b
=> nil
irb(main):020:0> ma == mb
=> true
irb(main):021:0> mb = c.method:)b)
=> #<Method: C#b>
irb(main):022:0> ma == mb
=> false

Ryan
 
G

gwtmp01

I believe it's the other way around: aliasing a method name just adds
another name for the same method. If you then redefine the original
method, the two names then point to different methods:

Like I said, I was probably missing something. What I was missing was
a correct understanding of how method redefinition occurs. I was
thinking of method redefinition as an operation applied to the
*existing*
method body. Sort of like changing the contents of a string. The
change is "visible" to anybody holding a reference to the string.

If I'm understanding correctly now, during method redefinition Ruby
constructs a brand new method body and then binds it to the existing
name thus breaking the binding made by the previous definition. If
there
are multiple bindings to the same method body (via alias) then those old
bindings still have access to the original method body.

Thanks for the clarification.


Gary Wright
 
R

Ryan Leavengood

If I'm understanding correctly now, during method redefinition Ruby
constructs a brand new method body and then binds it to the existing
name thus breaking the binding made by the previous definition. If there
are multiple bindings to the same method body (via alias) then those old
bindings still have access to the original method body.

Correct. In addition, as shown in my last email, any Method objects
created to reference the old method will still reference it, even after
a redefinition.

Ryan
 

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

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top