Skip to content(if available)orjump to list(if available)

Better Know a Ruby Thing: Singleton Classes

empw

I haven't written any ruby for over a decade and a half, when ruby 2.x was still on the horizon. But I do know the source of "eigenclass". It was first jokingly used in an odd instructional programming book / web comic / experimental art piece by "why the lucky stiff" who was briefly prominent in ruby-land then erased himself from the internet. It's funny that it has now become an established term of art for Ruby people.

lloeki

Sunny Ripert paid a wonderful tribute to _why as a talk at ParisRB 2020.

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.

Lio

I still refer to the * operator as “splat” after a presentation I saw him give in London in 2007.

_why is most definitely gone but not forgotten.

riffraff

I thought splat was actually the "official" usage these days, tho I think some people call it spread

byroot

It is yes [0].

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

Etheryte

It's called spread in Javascript [0], where the keyword is `...`.

[0] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...

dragonwriter

splat is the official name in Ruby, spread comes from the similar JS construct (obviously, lots of people using Ruby also use JS, so its natural for terminology from one context to sometimes get applied to similar concepts in the other even where it isn’t official.)

nixpulvis

I remember reading WPGTR in college. Really inspired me to play around and learn the language. I've always kept my printed copy in a safe place.

adriand

I read it after college when I first started working with Rails 1 after I joined a friend’s company. Funny to think that was almost two decades ago. I still remember the quirky art style, although I will admit to preferring a more prosaic book when I learned the language. But it was a fun community and the sense of creativity was really apparent.

insane_dreamer

loved _why’s stuff back when he was still around.

KerrAvon

_why's influence is indelible.

lcnPylGDnU4H9OF

> There are at least three ways to define a method in a Ruby singleton class.

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.

dragonwriter

> It's worth noting that two of such ways will, perhaps unintuitively, ignore the `private` keyword.

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.

[0] https://ruby-doc.org/3.4.1/Module.html#method-i-private

lcnPylGDnU4H9OF

Thanks for the correction! It can be hard to appreciate how true “everything is an object” is.

dragonwriter

That and how, between block arguments and optional parens on method invocation, Ruby allows (and the core exploits this) defining lots of constructs that look like keyword-based syntax as methods.

xutopia

I love it when someone dives a bit deeper behind something pretty common for most Ruby devs. It's actually a really elegant pattern that is used and it's everywhere in Ruby.

pansa2

> In Ruby, every object that is instantiated is represented internally by basically three things:

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

ryukoposting

Having embedded MRI into a variety of side projects over the years, I can confirm your evaluation of Ruby's method lookup procedure is accurate, at least in the context of that legacy, reference implementation of the language. I also agree that Ruby's design is deeply underappreciated.

mrinterweb

Don't forget about the Singleton module ("include Singleton"). This one trips me up in terms of what is being referred to as a "singleton". This is more of an singleton class instance where there can only be only one class instance per-class. https://ruby-doc.org/3.4.1/stdlibs/singleton/Singleton.html

Lammy

> What we’ve been calling “class methods” are actually instance methods of the metaclass.

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

inopinatus

Noel recommends def self.foo over class << self, implying that these are syntactic equivalents, but I beg to differ; the former does not update the third implicit context (aka the default definee) whilst the latter does, per yugui’s classic article on the matter https://blog.yugui.jp/entry/846, and this makes more sense to me in common usage.

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.

Alifatisk

I have to admit, years back when I learned Ruby I found this syntax to be very bizzare

  class User
    class << self
      def create_from_data
      end
    end
  end
Specifically, this: class << self

But when reading about it, it made sense

null

[deleted]

andrekandre

  > In Smalltal, you interact with your code through an code browser that is part of the system.
small spelling mistake: Smalltal → Smalltalk

rubyfan

Great read, I’ve written several ruby extensions in c and this write up made total sense.

null

[deleted]