Hubbry Logo
MetaclassMetaclassMain
Open search
Metaclass
Community hub
Metaclass
logo
7 pages, 0 posts
0 subscribers
Be the first to start a discussion here.
Be the first to start a discussion here.
Metaclass
Metaclass
from Wikipedia

In object-oriented programming, a metaclass is a class whose instances are classes themselves. Unlike ordinary classes, which define the behaviors of objects, metaclasses specify the behaviors of classes and their instances. Not all object-oriented programming languages support the concept of metaclasses. For those that do, the extent of control metaclasses have over class behaviors varies. Metaclasses are often implemented by treating classes as first-class citizens, making a metaclass an object that creates and manages these classes. Each programming language adheres to its own metaobject protocol, which are the rules that determine interactions among objects, classes, and metaclasses.[1] Metaclasses are utilized to automate code generation and to enhance framework development.[2]

Python example

[edit]

In Python, the builtin class type is a metaclass.[3][4][5] Consider this simple Python class:

class Car:
    make: str
    model: str
    year: int
    color: str

    def __init__(self, make: str, model: str, year: int, color: str) -> None:
        self.make = make
        self.model = model
        self.year = year
        self.color = color

    @property
    def description(self) -> str:
        """
        Return a description of this car.
        """
        return f"{self.color} {self.make} {self.model}"

At run time, Car itself is an instance of type. The source code of the Car class, shown above, does not include such details as the size in bytes of Car objects, their binary layout in memory, how they are allocated, that the __init__ method is automatically called each time a Car is created, and so on. These details come into play not only when a new Car object is created, but also each time any attribute of a Car is accessed. In languages without metaclasses, these details are defined by the language specification and can't be overridden. In Python, the metaclass - type - controls these details of Car's behavior. They can be overridden by using a different metaclass instead of type.

The above example contains some redundant code to do with the four attributes make, model, year, and color. It is possible to eliminate some of this redundancy using a custom metaclass. In Python, a metaclass is most easily defined as a subclass of type.

from typing import Any, Dict, Tuple

class AttributeInitType(type):
    def __call__(self, *args: Tuple[Any], **kwargs: Dict[str, Any]) -> object:
        """
        Create a new instance.
        """

        # First, create the object in the normal default way.
        obj: object = type.__call__(self, *args)

        # Additionally, set attributes on the new object.
        for name, value in kwargs.items():
            setattr(obj, name, value)

        # Return the new object.
        return obj

This metaclass only overrides object creation. All other aspects of class and object behavior are still handled by type.

Now the class Car can be rewritten to use this metaclass. In Python 3 this is done by providing a "keyword argument" metaclass to the class definition:

class Car(object, metaclass=AttributeInitType):
    @property
    def description(self) -> str:
        """
        Return a description of this car.
        """
        return " ".join(str(value) for value in self.__dict__.values())

The resulting object Car can be instantiated as usual, but can contain any number of keyword arguments:

new_car: Car = Car(make='Toyota', model='Prius', year=2005, color='Green', engine='Hybrid')

In Smalltalk-80

[edit]
The Smalltalk-80 metaclass hierarchy as a UML diagram
Diagram of the inheritance and instance relationships between classes and metaclasses in Smalltalk

In Smalltalk, everything is an object. Additionally, Smalltalk is a class based system, which means that every object has a class that defines the structure of that object (i.e. the instance variables the object has) and the messages an object understands. Together this implies that a class in Smalltalk is an object and that, therefore a class needs to be an instance of a class (called metaclass).

As an example, a car object c is an instance of the class Car. In turn, the class Car is again an object and as such an instance of the metaclass of Car called Car class. Note the blank in the name of the metaclass. The name of the metaclass is the Smalltalk expression that, when evaluated, results in the metaclass object. Thus evaluating Car class results in the metaclass object for Car whose name is Car class (one can confirm this by evaluating Car class name which returns the name of the metaclass of Car.)

Class methods actually belong to the metaclass, just as instance methods actually belong to the class. When a message is sent to the object 2, the search for the method starts in Integer. If it is not found it proceeds up the superclass chain, stopping at Object whether it is found or not.

When a message is sent to Integer the search for the method starts in Integer class and proceeds up the superclass chain to Object class. Note that, so far, the metaclass inheritance chain exactly follows that of the class inheritance chain. But the metaclass chain extends further because Object class is the subclass of Class. All metaclasses are subclasses of Class.

In early Smalltalks, there was only one metaclass called Class. This implied that the methods all classes have were the same, in particular the method to create new objects, i.e., new. To allow classes to have their own methods and their own instance variables (called class instance variables and should not be confused with class variables), Smalltalk-80 introduced for each class C their own metaclass C class. This means that each metaclass is effectively a singleton class.

Since there is no requirement that metaclasses behave differently from each other, all metaclasses are instances of only one class called Metaclass. The metaclass of Metaclass is called Metaclass class which again is an instance of class Metaclass.

In Smalltalk-80, every class (except Object) has a superclass. The abstract superclass of all metaclasses is Class, which describes the general nature of classes.

The superclass hierarchy for metaclasses parallels that for classes, except for class Object. ALL metaclasses are subclasses of Class, therefore:

  • Object class superclass == Class.

Like conjoined twins, classes and metaclasses are born together. Metaclass has an instance variable thisClass, which points to its conjoined class. Note that the usual Smalltalk class browser does not show metaclasses as separate classes. Instead the class browser allows to edit the class together with its metaclass at the same time.

The names of classes in the metaclass hierarchy are easily confused with the concepts of the same name. For instance:

  • Object is the base class that provides common methods for all objects; "an object" is an integer, or a widget, or a Car, etc.
  • Class is the base of the metaclasses that provides common methods for all classes (though it is not a metaclass itself); "a class" is something like Integer, or Widget, or Car, etc.
  • Metaclass provides common methods for all metaclasses.

Four classes provide the facilities to describe new classes. Their inheritance hierarchy (from Object), and the main facilities they provide are:

Object - default behavior common to all objects, like class access
Behavior - minimum state for compiling methods and creating/running objects
ClassDescription (abstract class) - class/variable naming, comments
Class - similar, more comprehensive, facilities to superclasses
Metaclass - initializing class variables, instance creation messages

In Ruby

[edit]

Ruby purifies the Smalltalk-80 concept of metaclasses by introducing eigenclasses, removing the Metaclass class, and (un)redefining the class-of map. The change can be schematized as follows:[6]

Smalltalk-80
Classes
Implicit
metaclasses
  
Terminal
objects
Ruby
Classes
Eigenclasses of
classes
Eigenclasses
of
eigenclasses
Terminal
objects
Eigenclasses of
terminal objects

Note in particular the correspondence between Smalltalk's implicit metaclasses and Ruby's eigenclasses of classes. The Ruby eigenclass model makes the concept of implicit metaclasses fully uniform: every object x has its own meta-object, called the eigenclass of x, which is one meta-level higher than x. The "higher order" eigenclasses usually exist purely conceptually – they do not contain any methods or store any (other) data in most Ruby programs.[7]

The following diagrams show a sample core structure of Smalltalk-80 and Ruby in comparison.[8] In both languages, the structure consists of a built-in part which contains the circular objects (i.e. objects that appear in a cycle formed by a combination of blue or green links) and a user-part which has four explicit objects: classes A and B and terminal objects u and v. Green links show the child→parent relation of inheritance (with the implicit upward direction), blue links show the complementary member→container relation of instantiation (a blue link from x points to the least actual container of x that is the start point for the method lookup when a method is invoked on x). Gray nodes display the eigenclasses (resp. implicit metaclasses in the case of Smalltalk-80).

Smalltalk-80   Ruby
Implicit metaclasses in Smalltalk-80 - A sample structure Eigenclasses in Ruby - A sample structure

The diagram on the right also provides a picture of lazy evaluation of eigenclasses in Ruby. The v object can have its eigenclass evaluated (allocated) as a consequence of adding singleton methods to v.

According to the Ruby's introspection method named class, the class of every class (and of every eigenclass) is constantly the Class class (denoted by c in the diagram). Class, and Struct are the only classes that have classes as instances.[9] [disputeddiscuss] Subclassing of Class is disallowed. Following the standard definition of metaclasses we can conclude that Class and Struct are the only metaclasses in Ruby. This seems to contradict the correspondence between Ruby and Smalltalk, since in Smalltalk-80, every class has its own metaclass. The discrepancy is based on the disagreement between the class introspection method in Ruby and Smalltalk. While the map x ↦ x.class coincides on terminal objects, it differs in the restriction to classes. As already mentioned above, for a class x, the Ruby expression x.class evaluates constantly to Class. In Smalltalk-80, if x is a class then the expression x class corresponds to the Ruby's x.singleton_class – which evaluates to the eigenclass of x.

In Objective-C

[edit]
Diagram of the inheritance and instance relationships between classes and metaclasses in Objective-C. Note that Objective-C has multiple root classes; each root class would have a separate hierarchy. This diagram only shows the hierarchy for an example root class NSObject. Each other root class would have a similar hierarchy.

Metaclasses in Objective-C are almost the same as those in Smalltalk-80—not surprising since Objective-C borrows a lot from Smalltalk. Like Smalltalk, in Objective-C, the instance variables and methods are defined by an object's class. A class is an object, hence it is an instance of a metaclass.

Like Smalltalk, in Objective-C, class methods are simply methods called on the class object, hence a class's class methods must be defined as instance methods in its metaclass. Because different classes can have different sets of class methods, each class must have its own separate metaclass. Classes and metaclasses are always created as a pair: the runtime has functions objc_allocateClassPair() and objc_registerClassPair() to create and register class-metaclass pairs, respectively.

There are no names for the metaclasses; however, a pointer to any class object can be referred to with the generic type Class (similar to the type id being used for a pointer to any object).

Because class methods are inherited through inheritance, like Smalltalk, metaclasses must follow an inheritance scheme paralleling that of classes (e.g. if class A's parent class is class B, then A's metaclass's parent class is B's metaclass), except that of the root class.

Unlike Smalltalk, the metaclass of the root class inherits from the root class (usually NSObject using the Cocoa framework) itself. This ensures that all class objects are ultimately instances of the root class, so that you can use the instance methods of the root class, usually useful utility methods for objects, on class objects themselves.

Since metaclass objects do not behave differently (you cannot add class methods for a metaclass, so metaclass objects all have the same methods), they are all instances of the same class—the metaclass of the root class (unlike Smalltalk). Thus, the metaclass of the root class is an instance of itself. The reason for this is that all metaclasses inherit from root class; hence, they must inherit the class methods of the root class.[10]

C++ proposal

[edit]

Herb Sutter of the ISO C++ Committee, has proposed the inclusion of metaclasses to C++ (originally for C++20 using C++17 features), though this feature has still not been included.[11]

This feature would build off of C++ concepts (constraints on types and templates), reflection, and if constexpr. Its primary goal would be to expand C++ abstraction beyond defined vocabulary (class, struct, union, enum, etc.) to allow adoptable vocabulary (such as interface, value), for the purpose of a more liberal approach to programming free of rule-memorisation. This feature would also allow non-standard features (such as interfaces) to be expressed as core features within libraries, and eliminate the necessity of specialised compilers (such as Qt moc, C++/CX, etc.) to express specific information using standardised features.

Although C++26 adopted different means to represent reflection[12], the paper proposed $T to represent reflecting type T and $expr to represent reflecting an expression expr, based off of an older proposal for reflection.[13] For example, this would examine each member variable m in T for one named xyzzy, and inject an int named plugh, using a constexpr block with -> {} (for injecting code into the enclosing scope). It further proposed compiler-integrated diagnostics to emit compiler diagnostic messages (i.e. compiler.error("Some error", source_location)). Named concepts could be used to declare symbols, similar to assigning concrete classes to variables holding interface types in Java and C#.[14]

using std::meta::variable;

constexpr {
    for (variable m: $T.variables()) {
        if (m.name() == "xyzzy") -> { int plugh; }
    }
}

For example, the following would create a metaclass called interface which would behave similar to a Java interface:

using std::meta::function;

$class interface {
    ~interface() noexcept {
        // custom logic if necessary here...
    }

    constexpr {
        compiler.require($interface.variables().empty(), "interfaces may not contain data");
        for (function f: $interface.functions()) {
            compiler.require(
                !f.is_copy() && !f.is_move(),
                "Interfaces may not copy or move; consider a virtual clone instead"
            );
            if (!f.has_access()) {
                f.make_public();
            }
            compiler.require(f.is_public(), "Interface functions must be public");
            f.make_pure_virtual();
        }
    }
};

// now supports (similar to Java/C#):
interface Shape {
    int area() const;
    void scaleBy(double factor);
};

One could similarly emulate Project Valhalla Java value classes, by creating $class value which generates comparison operators and default constructors.

It would further support ad-hoc duck typing:

  • $T.is(M) evaluates to true if and only if applying M to T succeeds (i.e. T already defines M) and its resulting type has no new members not already present in T.
  • $T.as(M) generates a type identical to T but is additionally defined using the named metaclass M.

It would allow for "stronger typedefs": types that behave similar to the original type but are distinct types for overloading and cannot implicitly convert to/from the original type by default:

$class noop {}; // a no-op metaclass

// equivalent to: enum class Handle: int { ... };
using Handle = $int.as(noop);

The following list (but not limited to) metaclasses were proposed to be defined in the standard library:

  • interface: an abstract base class with all public virtual functions, no copy/move constructors or data members
  • base_class: a class designed to be inherited from with no copy/move constructors or data members
  • ordered et al., classes which support some comparison category (such as total ordering, equality comparison, etc.)
  • value, a value class with default constructor/destructor, copy/move constructors, comparison methods, and no virtual methods or protected members
  • plain_struct, sometimes called "plain old data structs" (similar to "Plain old Java objects")
  • flag_enum, an enum for representing flags storing values corresponding to bitwise OR enumerators

The paper proposes that this allows for abstractions to greatly decrease boilerplate by producing generated functions and customisable defaults, semantics, and constraints. It further abolishes the need to create new language feature proposals by allowing such features to be expressed using metaclass features.

Support in languages and tools

[edit]

The following are some of the most prominent programming languages that support metaclasses.

Some less widespread languages that support metaclasses include OpenJava, OpenC++, OpenAda, CorbaScript, ObjVLisp, Object-Z, MODEL-K, XOTcl, and MELDC. Several of these languages date from the early 1990s and are of academic interest.[15]

Java features java.lang.Class<T> for introspection, similar to metaclasses, but are not actually metaclasses.

Logtalk, an object-oriented extension of Prolog, also supports metaclasses.

Resource Description Framework (RDF) and Unified Modeling Language (UML) both support metaclasses.

Metaclasses were a proposed for a possible inclusion in future version of C++, but currently is not an included feature.[11]

See also

[edit]

References

[edit]
[edit]
Revisions and contributorsEdit on WikipediaRead on Wikipedia
from Grokipedia
In , a metaclass is a class whose instances are other classes, serving as a mechanism to define and customize the creation, structure, and behavior of classes themselves. This concept enables multi-level hierarchies beyond the traditional class-instance , allowing for advanced abstractions such as modeling or dimensions where classes act as values in higher-level systems. Metaclasses originated in languages like Smalltalk, where every class is an instance of a metaclass, and all metaclasses ultimately subclass the Class metaclass while being instances of Metaclass itself, forming a reflective object model that supports dynamic class modification. In modern implementations, such as Python, the default metaclass is type, which handles class creation through methods like __new__ and __init__, but custom metaclasses can override these to enforce properties like abstract base classes, automatic registration, or resource locking during class definition. Key features include preparing the class namespace via __prepare__, resolving method resolution order (MRO), and executing the class body, often used in frameworks for logging, interface checking, or implementing like Singleton without explicit . Languages supporting metaclasses, including Python, Smalltalk variants like , and experimental extensions in (e.g., MCJ), leverage them for , though they introduce complexities in type systems and that require careful formalization to ensure soundness.

Fundamentals

Definition and Core Concepts

In object-oriented programming, a metaclass is defined as a class whose instances are themselves classes, thereby specifying the creation, , and of those classes. This positions metaclasses as a higher-order construct that governs class-level operations, distinct from the instance-level behaviors managed by regular classes. Core concepts of metaclasses revolve around their role at the meta-level of , where they facilitate —allowing examination of class and —and modification of class creation, such as customizing instantiation methods or enforcing class-specific constraints. For example, a metaclass might intervene during class definition to add metadata or alter default behaviors, enabling dynamic extensions to the object-oriented system without altering base-level code. These capabilities support paradigms by treating classes as first-class entities that can be queried and adapted at runtime. A fundamental principle is that every class is an instance of a metaclass, establishing a recursive yet foundational layer in object-oriented systems where the metaclass mirrors and extends the regular . This ensures uniform treatment of classes as objects, promoting consistency in how object behaviors are defined across abstraction levels. To illustrate abstractly, consider a generic metaclass hierarchy:

Metaclass | | (defines behavior of classes) v Class | | (instances are classes, e.g., Object) v UserClass | | (instances are objects) v Instance

Metaclass | | (defines behavior of classes) v Class | | (instances are classes, e.g., Object) v UserClass | | (instances are objects) v Instance

Here, Metaclass is the root, with Class (such as a base class like Object) as its instance, and a UserClass as an instance of Class; this structure demonstrates how metaclasses enable layered control over class instantiation and properties. The concept of metaclasses originated in Smalltalk, where they were introduced to model classes as objects.

Distinction from Regular Classes

Regular classes in object-oriented programming serve as templates or blueprints for creating and defining the behavior of object instances, specifying their attributes, methods, and state management. In contrast, metaclasses operate at a higher level of abstraction, functioning as the "class of a class" by defining how classes themselves are created, structured, and behave. For instance, while a regular class might dictate instance initialization and method invocation for objects, a metaclass governs class instantiation, such as validating attributes during class definition or enforcing structural constraints like interfaces. This distinction can be analogized to blueprints: a regular class is a blueprint for constructing everyday objects, whereas a metaclass is a blueprint for constructing those blueprints, enabling customization of the class creation process itself. In systems like Smalltalk, every class is an instance of its metaclass, which handles class-side behaviors such as method storage and resolution for the class object. Similarly, in Python, the built-in type metaclass (the default for all classes) oversees class object creation via hooks like __new__ and __init__, allowing interventions in namespace preparation or method resolution order (MRO) that regular classes cannot perform. Conceptually, metaclasses delineate a meta-level boundary, focusing on class-level customizations rather than instance-level logic; for example, they can dynamically add methods to classes or enforce protocols (as in Python's abc.ABCMeta for abstract base classes), but they do not directly manage object runtime behavior. This separation ensures that metaclasses enhance reflection and extensibility without encroaching on the domain of regular classes, which remain dedicated to instance orchestration. A common pitfall in understanding metaclasses arises from conflating class methods with static methods: class methods are instance methods of the metaclass, bound to the class object and receiving the class as the first argument, whereas static methods are unbound functions attached to the class namespace without access to class or instance state. This confusion can lead to unexpected binding behaviors during class customization.

Metaclass Hierarchies and Inheritance

In object-oriented programming systems supporting metaclasses, the metaclass hierarchy forms a layered structure where a root metaclass, often termed a universal metaclass such as Class, serves as the superclass for all other metaclasses. This root enables metaclasses to inherit common behaviors related to class creation and introspection. Meanwhile, regular classes inherit instance behaviors from their superclasses in the standard object hierarchy, but each class is itself an instance of a metaclass, establishing a reflective chain where classes are treated as objects governed by higher-level metaclasses. Inheritance in metaclass allow a subclass to automatically inherit the metaclass of its superclass, ensuring that class-level modifications propagate consistently unless explicitly overridden by specifying a new metaclass. This override alters the subclass's creation process, such as customizing initialization or method addition, while preserving the inherited metaclass's influence on related subclasses. The support meta-inheritance, where behaviors defined in a superclass's metaclass apply to subclasses, facilitating uniform policy enforcement across the hierarchy without redundant declarations. A generic UML-style representation of the metaclass chain illustrates this structure as follows:

Metaclass (root) | Class | UserClass (metaclass) | UserClass (regular class) | userObject (instance)

Metaclass (root) | Class | UserClass (metaclass) | UserClass (regular class) | userObject (instance)

Here, arrows denote (solid for superclass relations) or instantiation (dashed for instance-of relations), showing the parallel hierarchies: the metaclass side mirrors the class side, converging at the root. These hierarchies enable meta-inheritance, promoting consistent behavior across class families by propagating metaclass-defined features like automatic or protocol observance, reducing code duplication and enhancing maintainability in reflective systems. For instance, a base metaclass can enforce rules that apply uniformly to derived classes via rules. In Smalltalk, singleton metaclasses further specialize this for individual classes.

Historical Development

Origins in Smalltalk

The concept of metaclasses emerged as part of the development of at PARC, where the language evolved through multiple iterations in the 1970s to realize a pure object-oriented model. While earlier versions like Smalltalk-76 treated classes as first-class objects with capabilities, the explicit metaclass mechanism was introduced in Smalltalk-80 to enable classes to have their own distinct behaviors and methods, addressing limitations in prior systems where all classes shared a uniform protocol under a single Class metaclass. This innovation was advocated by Adele Goldberg to resolve issues such as uninitialized instances created via messages like new, allowing for customized initialization without altering the . The design rationale centered on maintaining uniformity in the object model by making every class an instance of another class—its metaclass—thus treating classes themselves as manipulable objects. Dan Ingalls implemented this feature, ensuring that each metaclass is a singleton inheriting from ClassDescription, which provides shared behavior for class management while permitting class-specific methods, such as tailored instance creation protocols (e.g., Point x: 10 y: 20). This approach avoided code duplication and supported reflective capabilities, where metaclasses form a parallel hierarchy mirroring the instance class hierarchy, with Object class as the root. As Goldberg and Ingalls noted, metaclasses allowed "each class [to] be able to have its own methods," enhancing the system's extensibility without compromising its message-passing foundation. This introduction in Smalltalk-80, under the guidance of as the project's visionary leader, marked a pivotal refinement in , solidifying Smalltalk's influence on the paradigm shift from procedural to object-centric designs during the late 1970s and early 1980s. By formalizing metaclasses, the system enabled dynamic manipulation of class behaviors, contributing to Smalltalk's role as a foundational language for modern OOP concepts.

Evolution and Adoption in Other Languages

Following the foundational concepts introduced in Smalltalk during the , metaclasses began influencing other programming languages in the and , particularly those emphasizing dynamic object-oriented features. , developed in the early by and at Stepstone, incorporated metaclasses as an integral part of its from its . In , classes are treated as objects, with each class having an associated metaclass that handles class methods and enables for class objects themselves. This design supported patterns like class clusters, where abstract public classes mask private concrete implementations, allowing flexible subclassing without exposing internal details. In the , , created by and first released in 1995, adopted a metaclass-like mechanism through eigenclasses to simplify per-object and per-class customization. Eigenclasses in Ruby function as anonymous singleton classes attached to specific objects or classes, enabling the addition of unique methods without altering the broader —a feature inspired by Smalltalk's reflective capabilities but streamlined for Ruby's object model. This approach facilitated in dynamic contexts, influencing Ruby's emphasis on developer productivity. Python formalized metaclasses in version 2.2, released in December 2001, as part of unifying its type and class systems. The built-in type became the default metaclass for new-style classes, allowing developers to define custom metaclasses via the __metaclass__ attribute or to control class creation and modification at runtime. This integration marked a milestone in Python's evolution toward more powerful , drawing from Smalltalk and earlier experimental extensions like Python's own Extension Classes package. The spread of metaclasses significantly shaped dynamic languages throughout the and 2000s, promoting reflective and generative programming paradigms in systems like (via modules like Class::MOP) and (through prototypes and ES6 classes). However, adoption in static languages faced substantial hurdles, primarily concerns over and compile-time predictability. In languages like C++, where strict typing prevents runtime alterations to class structures, full metaclasses have been proposed but not standardized; early ideas emerged in the alongside , but concrete proposals, such as Herb Sutter's generative metaclasses initiative starting in 2017, highlight ongoing efforts to balance expressiveness with safety. Instead, static languages often rely on alternatives like templates or reflection APIs to achieve similar effects without compromising type guarantees.

Implementations in Dynamic Languages

In Smalltalk-80

In Smalltalk-80, each class is an instance of a unique metaclass, ensuring that classes themselves are treated as objects within the system. This metaclass is automatically generated when the class is created and serves as the sole instance of the metaclass, forming a parallel hierarchy to the instance class hierarchy. For example, the class Car is an instance of Car class, which inherits from Class (the metaclass of Object) and ultimately from Metaclass. This structure allows classes to inherit metaclass behavior uniformly, such as method storage and hierarchy management, while tailoring specific behaviors at the class level. The creation of a new class and its metaclass occurs through the subclass: message sent to an existing class, specifying details like instance variables, class variables, shared pools, and category. A representative syntax is:

[Car](/page/Car) subclass: #Sedan instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Vehicles'.

[Car](/page/Car) subclass: #Sedan instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Vehicles'.

This command instantiates Sedan as an instance of its anonymous metaclass (Sedan class), which inherits from Car class and shares the parallel hierarchy. Metaclasses in Smalltalk-80 are anonymous by default, referenced only via the class message sent to the class (e.g., Car class), and do not require explicit naming unless customized through additional compilation or modification. This anonymity simplifies the model while allowing full object-oriented treatment of classes. Metaclasses enable the definition of class methods, which operate on the class object rather than its instances, supporting operations like instance creation and initialization. These methods are stored in the metaclass's method dictionary and looked up via the metaclass inheritance chain, mirroring instance method lookup. For instance, a class method can be defined using the compilation syntax Car class >> newWithColor: aColor, which might create and initialize a Car instance with a specified color attribute. Metaclasses inherit standard class methods from Class, such as new and subclass:, ensuring consistent system-wide behavior. A practical example of metaclass usage is implementing instance counting at the class level without altering instance behavior. This can be achieved by overriding the new method in the metaclass and using a to track the count, leveraging the metaclass's isolation from instance variables. The following illustrates this for the [Car](/page/Car) class:

"Define a class variable in the metaclass for counting" Car class compile: 'InstanceCount ^InstanceCount ifNil: [0]'. "Override new in the metaclass" Car class compile: 'new InstanceCount := (InstanceCount ifNil: [0]) + 1. ^super new'. "Query method in the metaclass" Car class compile: 'numberOfInstances ^InstanceCount'.

"Define a class variable in the metaclass for counting" Car class compile: 'InstanceCount ^InstanceCount ifNil: [0]'. "Override new in the metaclass" Car class compile: 'new InstanceCount := (InstanceCount ifNil: [0]) + 1. ^super new'. "Query method in the metaclass" Car class compile: 'numberOfInstances ^InstanceCount'.

Here, Car new increments the count via the metaclass method, and Car numberOfInstances retrieves it, demonstrating how metaclasses encapsulate class-specific state and logic independently of instances. This approach aligns with Smalltalk-80's uniform object model, where such customizations are compiled directly to the metaclass.

In Python

In Python, metaclasses provide a mechanism to customize the creation and behavior of classes, integrating seamlessly with the language's where classes are instances of metaclasses. A class is declared with a custom metaclass using the metaclass keyword argument in the class definition, such as class MyClass(metaclass=MyMeta):, allowing the metaclass to intercept and modify the class creation process. If no metaclass is specified, the default metaclass is type, which handles standard class creation by resolving the method resolution order (MRO), preparing the , executing the class body, and producing the class object. This explicit assignment enables developers to define metaclasses as subclasses of type to override specific aspects of class construction. The primary methods for customizing class creation in a metaclass are __new__ and __init__. The __new__ method, inherited from type, is responsible for creating the class object itself and receives arguments including the metaclass, class name, base classes, and namespace dictionary; overriding it allows precise control over whether and how the class is instantiated, often returning a modified or entirely new class object. Following successful creation via __new__, the __init__ method initializes the newly created class object with the same arguments, enabling post-creation modifications such as adding or altering class attributes without altering the core creation logic. Additionally, metaclasses can override __prepare__ to customize the namespace mapping used during class body execution, defaulting to an empty ordered dictionary for predictable attribute ordering. A representative use of metaclasses is to reduce boilerplate in class initialization by automatically generating attribute-setting behavior from the __init__ signature. For instance, the AutoInit metaclass wraps a class's __init__ method to set instance attributes directly from positional and keyword arguments, eliminating the need for manual assignments like self.make = make.

python

from functools import wraps from inspect import signature def binds(f): @wraps(f) def __init__(self, *args, **kwargs): vars(self).update([signature](/page/Signature)(f).bind(None, *args, **kwargs).arguments) del self.self # Remove the 'self' binding added by [bind](/page/BIND) return __init__ class AutoInit(type): def __new__(cls, name, bases, [namespace](/page/Namespace)): if '__init__' in [namespace](/page/Namespace): [namespace](/page/Namespace)['__init__'] = binds([namespace](/page/Namespace)['__init__']) return type.__new__(cls, name, bases, [namespace](/page/Namespace)) class AttributeInitType(AutoInit): """Custom metaclass for automatic attribute initialization.""" pass class Car(metaclass=AttributeInitType): def __init__(self, make, model, year): """Initializer with no manual attribute assignments needed.""" pass # Usage my_car = Car('Toyota', 'Corolla', 2023) print(my_car.make) # Output: Toyota print(my_car.model) # Output: Corolla print(my_car.year) # Output: 2023

from functools import wraps from inspect import signature def binds(f): @wraps(f) def __init__(self, *args, **kwargs): vars(self).update([signature](/page/Signature)(f).bind(None, *args, **kwargs).arguments) del self.self # Remove the 'self' binding added by [bind](/page/BIND) return __init__ class AutoInit(type): def __new__(cls, name, bases, [namespace](/page/Namespace)): if '__init__' in [namespace](/page/Namespace): [namespace](/page/Namespace)['__init__'] = binds([namespace](/page/Namespace)['__init__']) return type.__new__(cls, name, bases, [namespace](/page/Namespace)) class AttributeInitType(AutoInit): """Custom metaclass for automatic attribute initialization.""" pass class Car(metaclass=AttributeInitType): def __init__(self, make, model, year): """Initializer with no manual attribute assignments needed.""" pass # Usage my_car = Car('Toyota', 'Corolla', 2023) print(my_car.make) # Output: Toyota print(my_car.model) # Output: Corolla print(my_car.year) # Output: 2023

This approach streamlines data-oriented classes by leveraging the metaclass to inject initialization logic at class creation time, promoting cleaner code while preserving the original __init__ signature for documentation and validation. For advanced scenarios involving , Python resolves metaclass conflicts by selecting the most derived metaclass from the base classes' metaclasses, ensuring a coherent hierarchy through subtype checks; if no single metaclass is compatible (i.e., none is a subclass of all others), a TypeError is raised to prevent ambiguous behavior. This resolution mechanism maintains consistency in the class's metaclass while allowing flexible inheritance patterns, as defined in PEP 3115, which standardized metaclass protocols in Python 3.

In Ruby

In Ruby, metaclasses are implemented through a mechanism known as eigenclasses, also referred to as singleton classes, which provide a lightweight way to attach instance-specific methods to any object without affecting its class or other instances. An eigenclass is an anonymous subclass of the object's class, created on demand when singleton methods are defined, and it serves as the immediate superclass in the method lookup chain for that object. This design emphasizes Ruby's dynamic nature, allowing runtime modifications that are isolated to individual objects or classes. The primary syntax for accessing and defining an eigenclass is class << obj, which opens the eigenclass of the specified object obj and allows method definitions within it. For example, to add a singleton method to an instance:

ruby

o = Object.new class << o def greet "Hello from singleton!" end end o.greet # => "Hello from singleton!"

o = Object.new class << o def greet "Hello from singleton!" end end o.greet # => "Hello from singleton!"

Here, greet becomes available only on o, not on other instances of Object. For classes themselves, which are objects inheriting from Class, the eigenclass is accessed using class << self within the class definition, enabling per-class methods (often called class methods) without relying on inheritance from a full metaclass hierarchy. An example is:

ruby

class Car class << self def factory "Building a new car" end end end Car.factory # => "Building a new car"

class Car class << self def factory "Building a new car" end end end Car.factory # => "Building a new car"

This approach attaches methods directly to the class's eigenclass, keeping behavior specific to Car without subclassing or altering Class. Ruby's eigenclass system simplifies the metaclass hierarchies found in languages like Smalltalk, avoiding explicit metaclass classes and cycles in inheritance while maintaining a clean, one-to-one mapping where every eigenclass is an instance of Class. In Smalltalk-80, metaclasses form a complex pseudotree with a dedicated Metaclass class as the root, leading to potential inheritance paradoxes and multiple roots; Ruby resolves this by using implicit, object-attached eigenclasses that integrate seamlessly into the single inheritance chain rooted at BasicObject. The following table illustrates the key structural differences:
AspectSmalltalk-80 MetaclassesRuby Eigenclasses
Hierarchy StructurePseudotree with explicit Metaclass root; potential cycles and multiple rootsLinear chain; all eigenclasses subclass Class; acyclic and single-rooted at BasicObject
Class MappingMetaclasses map to Metaclass via .rclassAll eigenclasses map to Class via .class
CreationExplicit subclasses for each classImplicit, anonymous on demand for any object
Inheritance BehaviorBroken in some implementations (e.g., Pharo, Squeak)Consistent, with eigenclass as immediate superclass in lookup
This simplification enhances Ruby's expressiveness while reducing complexity. Eigenclasses are commonly used for monkey patching, where developers extend core classes dynamically by adding methods to their eigenclasses, such as overriding [String](/page/String) behavior for specific needs in a project. They also power domain-specific languages (DSLs) by defining class-level methods that configure behavior, like in Rails routing or testing frameworks. To mitigate the risks of global monkey patching—such as unintended side effects across an application—Ruby 2.0 introduced refinements, which allow scoped extensions via modules that refine classes locally without permanent changes. A refinement is defined using Module#refine and activated with using, limiting modifications to the lexical scope:

ruby

module StringRefinement refine String do def shout upcase + "!" end end end using StringRefinement "Hello".shout # => "HELLO!" # Outside scope, "Hello".shout raises NoMethodError

module StringRefinement refine String do def shout upcase + "!" end end end using StringRefinement "Hello".shout # => "HELLO!" # Outside scope, "Hello".shout raises NoMethodError

Refinements integrate with eigenclasses by prioritizing refined methods in the lookup chain during activation, providing a safer alternative for dynamic modifications.

In Objective-C

In Objective-C, metaclasses form an integral part of the , where each class is paired with a corresponding metaclass that serves as the class of the class object itself. This pairing ensures that class objects, which represent the blueprint for creating instances, are treated as objects in their own right and can respond to messages. For instance, if a class named Car inherits from Vehicle, which in turn inherits from NSObject, the metaclass of Car inherits from the metaclass of Vehicle, ultimately rooting in the metaclass of NSObject. This parallel hierarchy allows metaclasses to define and store class methods, enabling dynamic behavior for classes as objects. Metaclasses in Objective-C are implicit and do not have explicit names in source code; they are generated by the compiler and runtime during class definition. Access to a metaclass is achieved by sending the class message to a class object, such as [Car class], which returns the metaclass instance for Car. For dynamic introspection at runtime, functions like NSClassFromString and objc_getClass retrieve a class object by name from a string (e.g., Class carClass = NSClassFromString(@"Car");), after which the metaclass can be obtained via [carClass class]. These mechanisms support runtime queries and modifications, including checking if an object is a metaclass using class_isMetaClass. Additionally, objc_getMetaClass directly returns the metaclass for a given class name. A key application of metaclasses is runtime introspection and modification, exemplified by method swizzling, which exchanges implementations of class or instance methods dynamically. This is facilitated by runtime functions like method_exchangeImplementations, allowing developers to alter behavior without subclassing, often used in debugging or . Metaclasses also handle class initialization through the +initialize method, a class method invoked automatically once per class (on its metaclass) before any other class or instance methods are called. For example:

objc

+ (void)initialize { if (self == [MyClass class]) { // Perform one-time setup for the class, such as registering defaults. } }

+ (void)initialize { if (self == [MyClass class]) { // Perform one-time setup for the class, such as registering defaults. } }

This ensures safe, of class-level state.

Proposals and Support in Static Languages

C++ Metaclass Proposals

In , proposed metaclasses as a new language feature for C++ to enable more powerful compile-time code generation and , allowing users to define custom class categories with built-in behaviors such as interface or value semantics. This initial paper, P0707R0, introduced two primary metaclass types: interface metaclasses, which generate abstract base classes with pure virtual public functions and prohibit data members or copy/move operations to mimic or C# interfaces; and value metaclasses, which automatically provide default constructors, copy/move semantics, and equality comparisons for regular types like pairs or strings. The motivation was to reduce boilerplate in library design, such as implementing a standard interface metaclass in just 10 lines versus the extensive specifications required in other languages, while adhering to C++'s zero-overhead principle through compile-time expansion. The proposed syntax in P0707R0 used a declarative style with special keywords, exemplified by defining an interface metaclass as $class interface { public: $virtual ~interface() = default; template<class T> bool isa(); }; and applying it via interface Shape { int area(); };, which expands at into a full abstract class with virtual destructor and the specified pure virtual function. Similarly, a metaclass could be defined to generate types with canonical behaviors, such as value Pair { T first; U second; }; producing a class with defaulted special member functions and operator==. These metaclasses were intended to complement emerging features like concepts, enabling library authors to create extensible type systems without relying solely on templates or macros. A prototype implementation was demonstrated using a modified Clang compiler, showcasing generative capabilities for patterns like observer or singleton. The metaclass proposal was not adopted for or , as the standards committee prioritized foundational features including for template constraints in and modules for better encapsulation in both standards, deferring advanced enhancements. Subsequent revisions of P0707, such as R3 in 2018 and R4 in 2019, refined the idea by assuming support for static reflection (e.g., via P0578) and compile-time programming (e.g., via P0633), shifting focus toward "metaclass functions" as consteval templates that generate code. In the 2020s, the proposal evolved further, with P0707R5 in October 2024 presenting metaclasses as atop reflection facilities like P2996 (static reflection) and P3294 (), using concise notation such as class(metafunc) Widget { /* declarations */ }; to invoke a consteval for class generation. This version supports multiple s per class (e.g., class(xxx, yyy) Widget { ... };) and leverages tools like cppfront for full compiler support across MSVC, GCC, and , emphasizing declarative authoring with automatic defaults and constraints. While the core metaclass concept has transitioned from a standalone to a library-friendly extension of reflection—though it was not included in C++26 following the feature freeze, it remains under active development for potential inclusion in future standards without adoption in prior standards.

Reflection Mechanisms as Alternatives

In static languages lacking native metaclass support, reflection mechanisms provide partial alternatives by enabling and limited manipulation of types at compile-time or runtime, though they fall short of the full dynamic class modification offered by metaclasses. These approaches allow developers to query and interact with class metadata without altering the core language's static model. The C++26 standard includes compile-time reflection through proposal P2996, which provides the std::meta::info type and the ^^ reflection operator for accessing type information and object properties during compilation (adopted June 2025). This facility supports operations like enumerating members of a type or substituting values into templates, serving as a foundation for tasks such as automatic or trait generation, but it does not enable runtime class alterations or true metaclass . In , the Class<T> class acts as a reified representation of types, facilitating runtime via methods like Class.forName() to load classes dynamically and access their fields, methods, and constructors through the java.lang.reflect package. This supports tasks such as and plugin systems by allowing code to examine and invoke members reflectively, yet it cannot create or modify classes on the fly, restricting it to observation and invocation rather than generative . Other languages extend reflection further toward metaclass-like capabilities. Similarly, Scala's implicits (now givens in Scala 3) provide partial substitutes via type classes, which attach behavior to types ad-hoc without subclassing, supporting polymorphic extensions that mimic some metaclass functionalities like implicit conversions and method resolution. Despite these advancements, reflection mechanisms in static languages inherently limit dynamic class modification due to and performance constraints, often requiring or external tools for anything approaching the seamless extensibility of true metaclasses, as seen in their inability to support or incremental protocol extensions without recompilation.

Applications and Uses

Metaprogramming and Code Generation

Metaclasses facilitate by allowing programmers to intercept and customize the process of class creation, enabling dynamic modifications to class definitions at runtime or . In this approach, a metaclass overrides methods like __new__ or equivalent hooks to inspect, alter, or generate class attributes and methods before the class is finalized. For instance, during class instantiation, the metaclass can validate the structure of the class body, ensuring required attributes are present or enforcing specific design constraints. One key technique involves injecting methods or attributes into the class namespace to automate common behaviors, such as automatically generating accessor methods or validation logic based on declarative annotations. This is particularly useful in scenarios like object-relational mapping, where a metaclass can analyze class fields and produce corresponding database interaction code, such as SQL queries, without explicit implementation in the class itself. Similarly, metaclasses can enforce by overriding creation mechanisms; for example, to implement a , the metaclass modifies the class's constructor to return a single instance, preventing multiple instantiations. Metaclasses also support code generation for paradigms, where high-level descriptions expand into . By preparing a custom during class body evaluation—via hooks like __prepare__—the metaclass can track order, resolve forward references, or dynamically synthesize methods from simple declarations, transforming concise definitions into fully functional implementations. In languages like Python, this mechanism enhances expressiveness by allowing classes to be defined declaratively while the metaclass handles expansion. Another abstract example is adding cross-cutting concerns, such as , where the metaclass scans the class for methods and injects wrapper functions that log calls without altering the original . This reduces manual repetition, as developers declare intent once, and the metaclass propagates the behavior across the . Overall, these capabilities promote reusable abstractions, minimizing boilerplate and fostering domain-specific languages by centralizing customization logic at the metalevel.

Frameworks, Libraries, and Tools

In frameworks, metaclasses play a crucial role in automating object-relational mapping (ORM) for database interactions. In Django, the ModelBase metaclass is employed during model class creation to configure database table names, process field definitions, and establish relationships, enabling seamless integration with the ORM system. Similarly, SQLAlchemy's declarative base relies on the DeclarativeMeta metaclass to intercept attribute assignments, such as mapped_column() calls, and dynamically append them to underlying Table and Mapper objects for runtime configuration. ' ActiveRecord framework leverages Ruby's metaclass (eigenclass) mechanisms through techniques like macro-style association declarations (e.g., has_many), which dynamically inject methods into model classes to handle database persistence without explicit boilerplate. Among libraries, Python's abc module utilizes the ABCMeta metaclass to enforce abstract base classes (ABCs), allowing developers to define interfaces with abstract methods via the @abstractmethod decorator and register virtual subclasses for type checking and polymorphism. This metaclass enables runtime verification that concrete subclasses implement required methods, promoting robust code design in object-oriented hierarchies. In modeling tools, metaclasses underpin formal languages for metadata representation. The Meta Object Facility (MOF), standardized by the (OMG), defines the Class metaclass within its Essential MOF (EMOF) and Complete MOF (CMOF) layers, reusing UML 2.5 constructs to model classes with properties, operations, and associations while enforcing constraints like singleton instances for metamodel elements. Analogously, in the (RDF) Schema, rdfs:Class serves as a metaclass by classifying resources that are themselves RDF classes, supporting subclass hierarchies via rdfs:subClassOf and instance typing with rdf:type to enable extensible semantic modeling.

References

Add your contribution
Related Hubs
User Avatar
No comments yet.