In this section, we briefly review the notion of procedural reflection [des Rivières and Smith, 1984] in programming languages. A reflective programming language incorpo- rates a model of (aspects of) the language’s implementation and state of program exe- cution in the language itself, and provides facilities to manipulate this representation. Critically, changes in the underlying implementation are reflected in the model, and ma- nipulation of the representation by a program results in changes in the underlying imple- mentation and execution of the program. Perhaps the best known reflective programming language is 3-Lisp [Smith, 1984].
2.5.1 A computational view
One way to understand procedural reflection is to view computation as relations on syn- tactical objects and runtime objects of programming languages. Here, syntactical objects of a programming language appear in the source code of programs which are written in the programming language. For example, the source code of a Jason program contains the textual representations of beliefs, events and plans which are the syntactical objects of Jason. When the source code of a program is parsed by an interpreter, syntactical objects in the source code are converted into internal representations which are called runtime objects. Runtime objects are created by using suitable data structures (defined in another programming language which implements the interpreter). For example, in the Java im- plementation of the Jason interpreter, there is a class P lan for storing Jason plans; when the interpreter parses a Jason agent program, each plan (a syntactical object) gives rise to a Java object (a runtime object) of the class P lan.
In this computational view, there are two types of relations on syntactical objects and runtime objects. First, relations from syntactical objects to runtime objects are called in- ternalisations. This is a mapping which converts textual representations from programs of a language into runtime objects as internal representations within an interpreter as pro-
grams are parsed. Second, relations from runtime objects to runtime objects are called normalisations. This is a mapping which evaluates internal representations into their sim- plest form. For example, the evaluation of query, the application of plans, and the execu- tion of an action in Jason. In particular, the context query of a plan in Jason is evaluated into a simplest value of truth – either true or false – in order to determine if the plan can be applied to generate an intention or not. The application of a plan results in a new in- tention for the agent which is a sequence of actions. Then, the execution of an action of an intention results either changes to the environment (in case of an external action) or to states of an agent (in case of belief update actions and subgoal actions).
position(room1) +parcel(room2) position(X) +parcel(X) ∶ → GoT o(X); ⟨Belief⟩ ⟨Event⟩ ⟨Query⟩ ⟨T ruth⟩ ⟨P lan⟩ ⟨Intention⟩ ⟨Intention⟩ position(Y ) P ickP arcel
Syntactical objects Runtime objects
Figure 2.3: A computational view for procedural reflection.
Figure 2.3 illustrates the above computational view of agent programs in Jason. In the left hand side box are examples of syntactical objects while examples of runtime objects are grouped in the right hand side box. The notation ⟨T ⟩ denotes a runtime object of the Java class T defined in the implementation of the interpreter Jason. Furthermore, internalisations from syntactical objects are drawn as dashed arrows while normalisations are drawn in solid arrows.
2.5.2 Reflection in programming languages
Implementing an interpreter for a programming language can be considered as imple- menting internalisations and normalisations in this language. Then, adding procedural reflection to the programming language involves proving facilities to influence these inter- nalisations and normalisations. To this end, des Rivières and Smith defined two levels of reflection in a programming language, namely structural reflection and procedural reflection
Structural reflection: a language has structural reflection if it is provided with means to query and modify runtime objects. For example, one can define in a Lisp program a function which can modify the definition of other functions. When this function is applied to another function, it changes the internal representation of the other function. Thus, this allows one to influence internalisations.
Procedural reflection: a language has procedural reflection if it has structural reflection and additionally is provided with means to access the execution context of a pro- gram. Thus, this allows one to influence normalisations.
Many agent programming languages also provide support for procedural reflection. For example, PRS [Georgeff and Lansky, 1987] incorporated a meta-level, including re- flection of some aspects of the state of the execution of the agent such as the set of appli- cable plans, allowing a developer to program deliberation about the choice of plans in the language itself. Similarly, languages such as Jason provide facilities to manipulate sets of beliefs and intentions such as belief update actions for adding and removing beliefs. However, these languages do not provide facilities to program steps in a deliberation cy- cle such as different strategies to select applicable plans for generating new intentions or to select intentions for execution. Therefore, the support for procedural reflection in cur- rent state-of-the-art agent programming languages is often up to the level of structural reflection only.