How should metaclass work?

  • Posted By Stuart Halloway on February 12, 2008

Facets defines metaclass like this:

def meta_class(&block)
  if block_given?
    (class << self; self; end).class_eval(&block)
  else
    (class << self; self; end)
  end
end
alias_method :metaclass, :meta_class

RSpec defines it this way:

def metaclass
  class << self; self; end
end

I just spent an hour figuring out why some carefully-tested code went no-op after adding RSpec to a project. As a community we need to commit to a standard definition here. What should it be?

Comments
  1. Paul BarryFebruary 13, 2008 @ 12:31 AM

    I agree, metaclass should be part of Ruby and given those two options, I can’t see why anyone would prefer the RSpec way over the Facets way.

  2. David ChelimskyFebruary 13, 2008 @ 02:06 AM

    Agreed there should be a standard definition. And it should really live in the language, not in assorted libraries. I think you’ll find it in a few others besides Facets and RSpec.

    I’m going to move RSpec’s into a module that gets included where RSpec uses it so this won’t get in your (or anybody’s) way.

    Clearly the one in Facets is more general purpose, so of these two I’d vote for that (for inclusion in a general purpose library).

  3. Glenn VanderburgFebruary 13, 2008 @ 03:21 AM

    I’ll disagree with Paul and David. metaclass should be an accessor only, and be paired with metaclass_eval (to mirror class and class_eval).

    Why’s metaid gem almost gets this right:

    def metaclass; class << self; self; end; end
    def meta_eval &blk; metaclass.instance_eval &blk; end
  4. David ChelimskyFebruary 13, 2008 @ 03:23 AM

    To be clear – the metaclass method in RSpec is not intended for general consumption. It was a simple refactoring away of duplication within RSpec’s code and does exactly what is needed internally within RSpec. It was a mistake to put it in Object, and I’m moving it from there. But please don’t refer to it as “the RSpec Way.” Pretty please?

  5. Jim WeirichFebruary 13, 2008 @ 04:48 AM

    Terminology Objection: A “Metaclass” is an object that defines the behavior of a class. What the “metaclass” method above returns is the singleton class of an object (also sometimes called the “eigenclass”).

    In as far as Ruby has metaclasses, you could possibly refer to the eigenclass of a Class object as a metaclass.

    In summary, even though all metaclasses are eigenclasses, not all eigenclasses are metaclasses.

    This is probably a hopeless attempt to keep some terminology straight, but at least I tried, right?

  6. David ChelimskyFebruary 13, 2008 @ 06:08 AM

    @Jim – I’m all for getting the words right. I just wish there were sexier words than “singleton class,” which makes me think of that awful pattern, and “eigenclass,” which makes me feel like I need another degree.

    @Glenn – having two methods does make things more clear and honors command/query separation, so +1. I’m curious as to why you say why’s gem almost gets it right? Is it just the name? Of course, Jim would say he got both names wrong :)

  7. Glenn VanderburgFebruary 13, 2008 @ 02:38 PM

    Jim, part of me agrees with the terminology correction, and I’ve always liked the term ‘eigenclass’—but I suspect we’ve lost the battle at this point. (Note that even Why, who coined the term ‘eigenclass’, calls the method ‘metaclass’.)

    David, the only thing I think Why got wrong is the name ‘meta_eval’ as opposed to ‘metaclass_eval’ (again, looking for name parallelism with ‘class’ and ‘class_eval’).

  8. Rob SanheimFebruary 13, 2008 @ 03:18 PM

    +1 on Glenn’s suggestion. Ruby has enough unnecessary variation in the api…consistency is a good thing, and Ruby is flexible enough where you can define your own aliases if it makes you happy.