Feature #6478

BasicObject#__class__

Added by Thomas Sawyer almost 2 years ago. Updated over 1 year ago.

[ruby-core:45180]
Status:Feedback
Priority:Normal
Assignee:Yukihiro Matsumoto
Category:core
Target version:next minor

Description

How else is one supposed to get the class of a subclass of BasicObject?

History

#1 Updated by Yusuke Endoh almost 2 years ago

  • Status changed from Open to Feedback

I don't understand you.

Yusuke Endoh mame@tsg.ne.jp

#2 Updated by Thomas Sawyer almost 2 years ago

=begin
Sorry, I'll be more specific via example:

class Foo < BasicObject
end

foo = Foo.new

foo.class #=> raises NoMethodError

How to get class?

I suggest adding #class feature if there is no current means.
=end

#3 Updated by Yusuke Endoh almost 2 years ago

  • Status changed from Feedback to Assigned
  • Assignee set to Yukihiro Matsumoto

Okay, thanks. I assign this to matz.

Yusuke Endoh mame@tsg.ne.jp

#4 Updated by Nobuyoshi Nakada almost 2 years ago

  • Status changed from Assigned to Feedback
  • Target version changed from 1.9.3 to 2.0.0

=begin
((Why)) do you need it?

BTW, it's possible with pure-ruby.

class Foo < BasicObject
include ::Kernel.dup.moduleeval {
alias
method(:class, :class)
undefmethod *(instancemethods - [:class, :object_id])
self
}
end

p Foo.new.class
=end

#5 Updated by Thomas Sawyer almost 2 years ago

=begin
To ensure proper functionality when creating new instances from subclasses.

class Foo < BasicObject
def initialize(stuff)
@stuff = stuff
end
def dup
class.new(@stuff)
end
end

class Bar < Foo
end

We can't use (({Foo})) in dup, otherwise Bar would not be right.
=end

#6 Updated by Thomas Sawyer almost 2 years ago

"BTW, it's possible with pure-ruby."

That's a rather nasty implementation. Is there no better way than that? I tried binding Kernel method but that didn't work, obviously, b/c BasicObject isn't "an instance of Kernel".

#7 Updated by Nobuyoshi Nakada almost 2 years ago

=begin
Seems what you want is (({dup})), not (({class})).

class Foo < BasicObject
mix ::Kernel, dup: :dup, clone: :clone
end
=end

#8 Updated by Thomas Sawyer almost 2 years ago

That was just one example. Here, you can look at this for more cases:

https://github.com/rubyworks/ostruct2/blob/master/lib/ostruct2.rb

Just ctrl-f for class.

But what's this about "mix"? What Ruby are you running!? This is interesting, b/c I was thinking that I could use #respond_to? and I don't see anyway to add it to my BasicObject subclass except the "nasty" approach you demonstrated earlier.

#9 Updated by Nobuyoshi Nakada almost 2 years ago

=begin
(({Module#mix})) is a feature introduced last year, but may be removed from 2.0.
=end

#10 Updated by Benoit Daloze almost 2 years ago

nobu (Nobuyoshi Nakada) wrote:

Seems what you want is (({dup})), not (({class})).

class Foo < BasicObject
mix ::Kernel, dup: :dup, clone: :clone
end

But that would include all methods from Kernel with the current behavior of #mix, as mix ::Kernel would do.
So you need to opt-out all methods:

class Foo < BasicObject
  meths = (::Kernel.instance_methods - [:dup])
  mix ::Kernel, meths.each_with_object(dup: :dup) { |m,h| h[m] = nil }
end

(And Foo.new.dup fails with "undefined method `initialize_dup'")

That behavior of #mix is not very intuitive I think, what do you think about:

diff --git a/class.c b/class.c
index 8e637c0..e9d7a7e 100644
--- a/class.c
+++ b/class.c
@@ -769,8 +769,9 @@ domixmethodi(stdatat key, stdatat value, stdatat arg)
st
table *aliasing = argp->aliasing;
stdatat old, alias;

  • if (aliasing && st_lookup(aliasing, ID2SYM(id), &alias)) {
  • if (NILP(alias)) return STCONTINUE;
  • if (aliasing) {
  • if (!stlookup(aliasing, ID2SYM(id), &alias) || NILP(alias))
  • return STCONTINUE; id = rbtoid(alias); } if (stlookup(argp->mtbl, id, &old)) {

(and corresponding changes for the three other functions).
That is, if a Hash of methods is given, only import these methods.

#11 Updated by Yutaka HARA over 1 year ago

  • Target version changed from 2.0.0 to next minor

#12 Updated by Nobuyoshi Nakada over 1 year ago

=begin
"Method transplanting" is introduced into 2.0, so you can write:
class Foo < BasicObject
include ::Module.new {
[:dup, :initializedup, :initializecopy].each {|m|
definemethod(m, ::Kernel.instancemethod(m))
}
}
end

I expect someone would make such method in (({Module})) as an external library.
=end

#13 Updated by Alexey Muranov over 1 year ago

Maybe BasicObject is not intended to be subclassed directly? Why not to subclass Object instead? I do not think it is wrong that basic objects do not know who their class is, after all they are basic.

Also available in: Atom PDF