Mixin puzzle


#1

given the below, if you were not allowed to modify class X

module N
def foo
end
end

module M

end

class X
extend M
end

would it possible for

x = X.new

to somehow call foo as an instance method of x?


#2

On Thu, 4 May 2006, polypus wrote:

end

class X
extend M
end

would it possible for

x = X.new

to somehow call foo as an instance method of x?

harp:~ > cat a.rb
module N
def foo() p 42 end
end
module M
include N
end

class X
include M
end

x = X.new
x.foo

harp:~ > ruby a.rb
42

-a


#3

class X
include M
end

thanks, but you modified class X, which was my provision that you should
not do.


#4

– Daniel

thanks, close but no cigar. notice that i want X.new to call foo. i
don’t think that it’s possible.


#5

the solution should read exactly as i wrote it, the only modifications
allowed are to the body of the two modules.


#6

On May 3, 2006, at 11:59 PM, polypus wrote:

would it possible for

x = X.new

module N
def foo() “foo” end
end
class X
end

x = X.new
class << x
include N
end
x.foo # => “foo”

– Daniel


#7

Obviously I modified more than the 2 modules. This is the best you’re
gonna do, cause I don’t think extend has anything that can act as a
“callback,” ala append_features

class Object
alias_method :old_extend,:extend
def extend(mod)
include(mod) if self == X
old_extend(mod)
end
end

module N
def foo
“N#foo”
end
end

module M
include N
end

class X
extend M
end

puts X.new.foo # N#foo


#8

On Thu, May 04, 2006 at 06:59:59AM +0900, polypus wrote:

end

class X
extend M
end

would it possible for

x = X.new

to somehow call foo as an instance method of x?

module N
class << self; attr_accessor :bleh end # just to prove #foo was run
self.bleh = “#foo not executed”
def foo
N.bleh = “#foo was executed!!”
end
end

module M
def self.extend_object(o)
o.class_eval do
include N
def self.new(*a,&b)
r = super(*a, &b) # explicit -> less stuff to remember
r.foo # is this what you meant?? weird.
# why won’t X.new.foo do?
r
end
end
end
end

===== unmodified =>

class X
extend M
end

x = X.new # => #<X:0xb7e2f034>

<=====

RUBY_VERSION # => “1.8.4”
N.bleh # => “#foo was
executed!!”

Now, care to explain the intended use of this?
Or is it just a pointless exercise?


#9

On Thu, 4 May 2006, polypus wrote:

class X
include M
end

thanks, but you modified class X, which was my provision that you should
not do.

sorry. i assumed since you said

class X
extend M
end

that you didn’t really mean it :wink:

-a


#10

I had no idea that extend has an append_features-style callback, you
learn something every day. Thanks Mauricio.


#11

On Thu, 4 May 2006, Mike H. wrote:

I had no idea that extend has an append_features-style callback, you
learn something every day. Thanks Mauricio.

i think you should elaborate on what you really want, what mauricio
posted,
though cool, definitely modifies the class:

harp:~ > cat a.rb
module N
class << self; attr_accessor :bleh end # just to prove #foo was run
self.bleh = “#foo not executed”
def foo
N.bleh = “#foo was executed!!”
end
end
module M
def self.extend_object(o)
o.class_eval do
include N
def self.new(*a,&b)
r = super(*a, &b) # explicit -> less stuff to remember
r.foo # is this what you meant?? weird.
# why won’t X.new.foo do?
r
end
end
end
end

===== unmodified =>

class X
p ancestors
extend M
p ancestors
end

harp:~ > ruby a.rb
[X, Object, Kernel]
[X, N, Object, Kernel]

what, exactly do you mean when you say the class must not be modified?
both
the solution i posted and this one modify the class in exactly the same
way :
by including a module in the class.

what you originally posted:

class X
extend M
end

would it possible for

x = X.new

to somehow call foo as an instance method of x?

iff you really do not modify the class X, can be accomplished thus:

harp:~ > cat a.rb
module N
def foo() p 42 end
end
module M
include N
end
class X; end
x = X.new
x.extend M
x.foo

harp:~ > ruby a.rb
42

any other way to be able to call x.foo will, in fact, modify the class
X. but
maybe that’s not what you really want?

regards.

-a


#12

ok for those who like pointless exercises here is a harder version.

rules:

  1. you may replace the elipses with any amount of code but may not alter
    any other part of the code listing
  2. when the program is run it must return, i.e. X.new must evaluate to
    an instance of class X
  3. foo must be called as an instance method of an instance of X and
    print something like #<X:0xb7e188a4>
  4. you may not use class_eval, module_eval, or use the extend_object
    callback

module N
def foo
puts self
end
end

module M

end

class X
extend M
end

X.new

yes there is a solution, good luck


#13

On May 4, 2006, at 10:14 PM, polypus wrote:

print something like #<X:0xb7e188a4>


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

module M
class ::X
include N
end
end


#14

ok i guess i need a rule 5, you may not mention class X in your code

module M
class ::X
include N
end
end

also you did not solve rule 3


#15

thanks a lot everybody, you have helped me simplify my library
interface.
mauricio you solved it. i knew about the extend callback and am already
in fact using it in the project from whence the very point-full exercise
was distilled, but i hadn’t thought of your solution, so thanks. unknown
(ara?) you’re right my definition of ‘not modify’ was not clear, i just
meant do not modify my code listing, while modifying class in memory was
fine. i’ll be releasing my project as a gem and will announce it on list
when i do so mauricio you’ll see your answer in action.

to illustrate my point, now the user of my lib can go

class X
extend M
end

instead of

class X
extend M
def initialize
init_lib
end
end

it’s not really a huge deal, but hey it’s one less thing for the user to
need to know/worry about. and i’ve always wondered if it was possible.


#16

On May 4, 2006, at 10:31 PM, polypus wrote:

also you did not solve rule 3


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

eh It was a joke answer anyway :-p


#17

hallelujah!

wasn’t that hard for you was it?
wondering whether it wouldn’t be a good one for rubyquiz though.


#18

On Fri, 5 May 2006, polypus wrote:

  1. you may not use class_eval, module_eval, or use the extend_object
    end

class X
extend M
end

X.new

yes there is a solution, good luck

harp:~ > cat a.rb
module N
def foo() puts self end
end

module M
def new(*a, &b)
obj = super
obj.extend N
obj.foo
obj
end
end

class X
extend M
end

X.new

harp:~ > ruby a.rb
#<X:0xb75d196c>

-a


#19

On May 4, 2006, at 10:31 PM, polypus wrote:

also you did not solve rule 3


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

Here’s some more whacky code:

% cat whackextend.rb
module M
$old_extend = Object.instance_method(:extend)
class ::Object
def extend(other)
if other.name == “M”
eval %Q{
class #{self}
include N
def initialize
foo
end
end
}
else
$old_extend.bind(self).call(other)
end
end
end
end

I’m pretty sure it follows all your rules.


#20

test