next up previous
Next: Implementing behavioral reflection Up: A Tutorial on Behavioral Previous: Introduction

   
Reflective programming languages

Reflective programming languages have existed since the seminal work of Smith in the early eighties, yet the area lacks a widely accepted account of what is reflection and what are reflective languages. Some confusion arises because reflective properties already appear in several existing languages. In this section, we establish our terminolgy for the basic concepts used throughout this paper. Because we are particularly interested in the efficient implementation of reflective programming languages in the large, our definitions are biased towards a better understanding of implementation issues (a companion paper [DM95] provides an historical and comparative overview of reflective concepts and languages), beginning with the following basics about programming languages.

Definition 2.1   A programming language is a medium to express computations, which is defined by its syntax and its semantics. An implementation of a programming language is the realization of its syntax and semantics, which comprises a translator and a run-time system. A program written in a particular programming language is a syntactically well-formed sequence of symbols that expresses a computation, which semantics is described by mapping its syntactic constructs to formal descriptions using the programming language semantics, and gathering them together to express its computation.

The effective computation is obtained by translating the program to a suitable low-level sequence of actions to be executed on a computer in conjunction with the language run-time system. When the translation is done on-line, expression by expression, and interleaved with the actual computation, the implementation is said to be an interpreter for the language; when it is done off-line, all at once, usually prior to the execution, the implementation is said to be a compiler for the language.

Given these basics, an effective or operational definition of reflection in the context of programming languages might well be the following:

Definition 2.2   Reflection is the integral ability for a program to observe or change its own code as well as all aspects of its programming language (syntax, semantics, or implementation), even at run-time. A programming language is said to be reflective when it provides its programs with (full) reflection.

The word integral in the above definition is very important. We insist on it to promote the idea that true reflection imposes no limit on what the program may observe or modify. This is clearly a long term goal. There are even theoretical limits (Gödel incompleteness theorem, paradoxical situations, etc.). But for the time being, we will stick to this definition to avoid including everything in reflection, and considering all languages as reflective. We will come back to this issue shortly.

In order to observe or change something, this thing must be represented in a such a way that the user program can manipulate it. This paves the way to the notion of reification.

Definition 2.3   Reification is the process by which a user program or any aspect of a programming language , which were implicit in the translated program and the run-time system, are brought to the fore using a representation (data structures, procedures, etc.) expressed in the language itself and made available to the program , which can inspect them as ordinary data. In reflective languages reification data are causally connected to the related reified information such that a modification to one of them affects the other. Therefore, the reification data is always a faithful representation of the related reified aspect.

Reification, at least partially, has been experienced in many languages to date: in early Lisps and in current Prologs, programs have been treated as data, although the causal connection has often been left to the responsability of the programmer. In Smalltalk-80, the compiler from the source text to bytecode has been part of the run-time system since the very first implementations of the language. Several other examples exist.

Notice the importance of the capability for the program to handle the reification data. This is in contrast with higher order languages for example, in which entities that were implicit are given a first-class status. First-class entites need only be denotable and used as any other primitive data types of the language: they may be passed as parameters, returned as results, assigned to variables. This is the case of functions and continuations in Scheme for example. But they need not be represented by data structures that can be inspected.

Most of the difficulties in giving a precise definition of what is reflection and what it is not stems from the fact that several existing languages exhibit some reflective behavior or provide reflective mechanisms, a notion that we define as follows:

Definition 2.4   A reflective mechanism is any means or tool made available to a program written in a language that either reifies the code of or some aspect of , or allows to perform some reflective computation.

It is difficult to draw a precise and tight frontier between what is considered as a few appearances of reflective mechanisms and full reflection. Indeed, the above definition is very general. It aims at gathering all expressions of reflective behavior or reflective properties in existing languages and systems. Our goal in introducing these distinctions is both to contrast selective expression of reflective properties from true (fully) reflective languages. On the other hand, we want to reuse the know-how developed around these mechanisms in existing languages and systems. For example, reification of program code has been done in several languages, but this does not mean that these languages were reflective. On the other hand, because reflection needs reified programs, the technology developed in Lisp, Prolog, Smalltalk and other languages is readily available for reflective languages.

Some reflective mechanisms are now pretty well-understood and their efficient implementation is also well-documented: the aforementioned programs reified as data 3, representation of program entities at run-time, for example using classes and metaclasses as first-class entites in object-oriented programming, etc. We will see later on that a lot of existing techniques provide a very helpful basis for the efficient implementation of reflective languages. In fact, the observation that the well-mastered cases lie much more in the static representation of programs side of reflection lead naturally to a distinction between structural and behavioral reflection.

Definition 2.5   Structural reflection is concerned with the ability of the language to provide a complete reification of both the program currently executed as well as a complete reification of its abstract data types. 2

Definition 2.6   Behavioral reflection, on the other hand, is concerned with the ability of the language to provide a complete reification of its own semantics and implementation (processor) as well as a complete reification of the data and implementation of the run-time system.

Pionnered by Smith [Smi82,Smi84], behavioral reflection has proved, as we noted before, to be much more difficult to implement efficiently than structural reflection. This is essentially because it raises crucial issues related to the execution of programs. First of all, behavioral reflection complicates the run-time model of the language by introducing reflective towers, as we will see shortly. When efficiency, and therefore compiling, come into play, the following questions are raised:

1.
How to represent the language semantics in such a way that programs can modify it?
2.
How to represent the run-time data in such a way as to provide programs with easy access to them while retaining efficiency?
3.
How to compile programs where the semantics of the language can be modified?
4.
How to compile programs where the implementation of the run-time system can be modified?


next up previous
Next: Implementing behavioral reflection Up: A Tutorial on Behavioral Previous: Introduction
Matt Hurlbut
1998-07-02