Better Know a Ruby Thing: Singleton Classes
noelrappin.com
noelrappin.com
https://m.youtube.com/watch?v=njr39cVU7d0
It was not so much aimed at the "old guard" (those that knew about _why) as the "next generation" (those that never heard of him).
At some point Sunny asked to raise hands if one knew about _why, maybe half did, tops.
By the end of the talk the emotion in the room was palpable.
_why is most definitely gone but not forgotten.
> You can turn an Array into an argument list with * (or splat) operator
> You can turn a Hash into keyword arguments with the * (keyword splat) operator:
It's also named spat across the MRI codebase, e.g. `VM_CALL_ARGS_SPLAT` etc.
[0] https://docs.ruby-lang.org/en/master/syntax/calling_methods_...
[0] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
It's worth noting that two of such ways will, perhaps unintuitively, ignore the `private` keyword.
private
def self.foo
# ...
end
def x.foo
# ...
end
That will define a public method `:foo` on each of `self`'s and `x`'s singleton classes. If you want the method to be private, either 1) explicitly make the method private using `Class#private` or `Class#private_class_method` or 2) use the `class << self` (or `class << x`) syntax in order for the `private` keyword to work as expected. private_class_method def self.foo
# ...
end
def self.foo
# ...
end
private_class_method :foo # `def` will return a symbol of the defined method name so the above syntax is usually sufficient.
def x.foo
# ...
end
x.singleton_class.send :private, :foo # Class#private is a private method(!) so must be called using Object#send.
class << x
private
def foo
# ...
end
end
> The eternal question… Why is Ruby like this?Joy! :) I'm pretty sure matz would agree.
No, this explanation is wrong, because there isn't actually a `private` keyword, only the Module#private method [0] which, when called with no arguments, changes the visibility of future methods defined in the target module.
Those examples call the `Module#private` method with no arguments in one context changing the visibility of future methods defined in that context, and then use an explicit prefix to define methods in a different context.
When you don't forget that `private` is a private instance method on Module and not a keyword, the behavior is much simpler to understand.
> * A table of instance values
> * A pointer to the class of the object, used for method lookup
> * A pointer to a unique class for that instance, called the “singleton class” of the object, also used for method lookup
I don't think this is accurate. The source code shows a generic `RObject` [0] contains flags, a pointer to a class (named `klass`), and instance variables. An `RClass` [1] contains flags, `klass`, `super` and a table of methods.
In both cases there is only a single pointer to a class that's used for method lookup.
-
> Ruby will look in the singleton class for the specific user instance first, and will only look for an instance method of the class if there is no matching method in the singleton class.
This is true, but it implies that Ruby does two separate lookups. The way it actually works is more elegant.
An object's `klass` points to its singleton class, and method lookup happens only on that class and its superclasses. The second lookup implied above happens implicitly, because the class of the object is an ancestor of the singleton class.
Specifically, for `class C`, its `klass` points to the singleton class `<< C`, whose ancestors are `<< Object`, `<< BasicObject`, `Class`, `Module`, `Object` and `BasicObject`.
IMO Ruby's whole object model, and in particular the design of this inheritance chain, is beautiful and underappreciated.
-
> I’m not really sure why the Smalltalk solution wasn’t used
I think the way Ruby actually works is closer to the Smalltalk approach than the author realizes.
-
[0] https://github.com/ruby/ruby/blob/71f402c5d50919b0329d04704d...
[1] https://github.com/ruby/ruby/blob/71f402c5d50919b0329d04704d...
This also shows up if you ever want to use a Refinement on a “class method”. The thing you must `refine` is the `singleton_class`.
Here's a live example from my UUID library where I refine `Time::now` and one of my own utility class' methods to test the time-went-backwards and network-card-changed cases for incrementing the sequence value while generating time-based UUIDs: https://github.com/okeeblow/DistorteD/blob/3e9bbc744479afd3e...
So instead of treating them as syntactic equals when they’re not, I reserve the former as a special case, a clear sign that the following method body was intended to operate in the implicit definition context of its surroundings for some reason.
class User
class << self
def create_from_data
end
end
end
Specifically, this: class << selfBut when reading about it, it made sense
> In Smalltal, you interact with your code through an code browser that is part of the system.
small spelling mistake: Smalltal → Smalltalk