Metaprogramming, Reflection, and Metaobject Protocols (MOPs)

Metaprogramming, Reflection, and Metaobject Protocols (MOPs) are fascinating computer science topics, exploring how programs can examine and modify themselves. However, the terminology can be unclear. This document presents my current understanding of these concepts.

While reading Axel Rauschmayer’s comprehensive article about metaprogramming in ES6 (this), I found the concept of “Reflective Metaprogramming” and its subcategories somewhat unclear. It differed from my previous understanding of Reflection and Metaprogramming.

Metaprogramming broadly refers to programs treating other programs as data. This includes reading, generating, analyzing, transforming, and even self-modification during execution. “Reflective Metaprogramming,” as described in the article, focuses on this self-interaction.

While I’ve always equated “Reflective Metaprogramming” with simply “Reflection,” my categorization differs slightly. Axel Rauschmayer describes:

  • Introspection: Read-only access to program structure.
  • Self-modification: Ability to change program structure.
  • Intercession: Ability to redefine language operation semantics.

In contrast, I traditionally used these categories:

  • Introspection: Same as above.
  • Structural Reflection: Runtime access and modification of properties, methods, and class creation. Most languages offer this to some degree, like .NET’s evolution from Reflection API to Roslyn.
  • Computational Reflection: A more complex concept involving modifying language semantics, including method resolution, property access, and even adding new language constructs at runtime. I associate this with MOPs.

Since “Structural” and “Computational Reflection” are less common terms than “Introspection,” “Self-modification,” and “Intercession,” I will try adopting the latter terminology.

The concept of a MOP is often debated. Here are some definitions I’ve encountered:

  • StackOverflow: Exposes internal interpreter structure to programmers, allowing inspection and behavior alteration through classes and methods. All objects in a MOP are metaobjects.
  • Wikipedia: Provides vocabulary for accessing and manipulating object structure and behavior. Functions include creating/deleting classes, methods, properties, modifying inheritance, and generating/modifying method code. MOPs expose the internal structure, contradicting the Open/Closed Principle, and are used sparingly, often for software transformation like reverse engineering.
  • Moose Documentation: An API for an object system, abstracting components like classes, objects, methods, and attributes. This enables introspection and manipulation of the described object system. Every system has an implicit MOP for automatic functions like method dispatch and inheritance. Explicit MOPs, used for introspection/reflection, are less common and range from restrictive (Java, C#) to open (CLOS).

The distinction between implicit and explicit MOPs remains unclear to me. Java and C# handle inheritance in a fixed manner, lacking APIs for modification (except dynamic C#). There are no APIs for intercepting method calls or property access (beyond proxies), no equivalents to method_missing, autoload, or noSuchMethod, and no way to modify inheritance chains or behavior. This starkly contrasts with the capabilities found in Groovy.

Groovy’s MOP provides hooks for altering class or object behavior, including:

  • getProperty/setProperty: Control property access.
  • invokeMethod: Control method invocation, allowing parameter tweaking or intercepting non-existent methods.
  • methodMissing: Intercept non-existent methods.
  • propertyMissing: Intercept non-existent properties.

Finally, Rauschmayer’s article describes the ECMAScript specification as containing a MOP for object handling, consisting of internal methods (denoted by double square brackets) inaccessible from JavaScript code. While ES6 proxies allow altering property access and method invocation behavior, they require updating all references to the proxy. In contrast, Groovy allows direct modification of an object’s protocol by redefining its invokeMethod method, maintaining transparency across references.

Given these varied perspectives, I propose this practical definition of a MOP:

A MOP is language support (API) for modifying property access, method invocation, and inheritance. This includes:

  • Basic features: Adding/modifying methods/properties, manipulating inheritance/prototype chains.
  • Advanced features: Modifying language syntax, exception propagation, loop behavior (e.g., parallelization), method synchronization/async behavior, memoization, throttling, etc.
Licensed under CC BY-NC-SA 4.0
Last updated on Sep 11, 2022 18:49 +0100