• Aucun résultat trouvé

Metalanguage as Language Definition and Metacircular Interpreters

Meta-Interpretation

8.1 Metalanguage as Language Definition and Metacircular Interpreters

The concept of a metalanguage as a “language about language” is a vague one and has led to it being used with several different meanings. The comment that

“metalogical has often been used where extralogical would be more appropriate” has been made [Weyrauch, 1980]. This point will be returned to later in this chapter.

With programming languages, the term was used early on to describe languages that could formally describe the meaning or behavior of the first high-level languages, inspired in particular by Algol [Naur, 1960], one of the first attempts to define a language formally first and practically second. It was also inspired by concern (see, for instance, [Feldman, 1966]) that others of the then newly developed high-level languages could only be genuinely considered “high-level” and could be safely ported between machines if they could be described in terms other than the assembly languages into which they compiled. If they could not and the assembly languages in turn could only be described in terms of the physical electronics of the machines, the languages were really only defined by the hardware. An important early conference on metalanguage for programming language description held in Vienna in 1964 [Steel, 1966] was considered then as “among the most valuable and productive scientific meetings ever held on a subject pertaining to information processing”. Note that the vagueness of the term metalanguage was already apparent at this stage. Some of the papers described metalanguages, which built on BNF (Backus Naur Form) and some explicitly noted the distinction between syntax and semantics. Our interest here will only be on the semantics side.

As mentioned in Chapter 2, Landin noted a correspondence between Algol and Church’s lambda calculus [Landin, 1965] and expounded further on it at this Vienna conference. At the same conference, McCarthy [1965] chose the different approach of describing Algol in terms of a mini-language he called “Micro-Algol”. Programs in this language consist only of assignments and conditional gotos of the form if p then goto a. McCarthy later developed this into a full metacircular interpreter; the idea was that since the interpreter was written in Micro-Algol and could interpret Micro-Algol, Micro-Algol was a language, which defined itself. Algol and any other high-level language that could be translated to Micro-Algol were thus fully defined.

The definition did not have to resort to a lowest-level description in terms of machine hardware (just as the self-interpreting USA constitution means that the USA legal

system does not ultimately rely on the whims of a monarch or the reasonableness of the middle classes).

The following is McCarthy’s [1965] Micro-Algol metacircular interpreter:

micro: n:=c(s,ξ);

if beyond(π,m) then goto done;

s:=statement(n,π);

ξ:=if assignment(s) then a(sn,n+1,a(left(s),value(right(s),ξ),ξ)) else if value(proposition(s),ξ) then a(sn,numb(destination(s),π),ξ) else a(sn,n+1,ξ);

goto micro;

done:

This requires some explanation. ξ is the state vector of the program π being interpreted. The state gives the value of all the variables and all other information that together with the program itself determines the future course of the computation.

Today this would be called a continuation. The pseudo-variable sn, whose value is included in ξ, gives the number of the statement, which is currently being executed.

a(var,value,ξ) gives the new state resulting from assigning value to variable var;

left(s) gives the variable on the left hand side of an assignment s, right(s) gives the expression on the right-hand side; value(e,ξ) evaluates expression e when the current state is ξ; proposition(s) gives p when s is if p then goto a while destination(s) gives a; numb(L,π) gives the statement number corresponding to label L in program π.

Landin’s compilation into lambda calculus was the more immediately practical. Since lambda calculus could be considered the machine code of the abstract SECD machine which Landin had also developed [Landin, 1963], this could in turn be implemented on a real machine, thus the approach could be the basis of a practical compiler. The important thing was the introduction of clarity into what was then something of a

“black-art” of compiler writing through this division into layers. The division was somewhat spoilt by the necessity to add further facilities to the SECD machine to cope with those aspects of Algol that could not easily be represented in lambda calculus. The development of programming languages that were purely sugared lambda calculus came later [Turner, 1979].

McCarthy’s approach was not designed to lead to a practical compiler, or even to an impractical one that could nevertheless be put forward as a working model to define the semantics of a language operationally; it was more an operation in defining semantics abstractly. An important distinction is that McCarthy’s interpreter makes the machine state of the language an explicit object in the metalanguage, whereas in Landin’s approach the implicit machine state of the language is translated to an implicit machine state in the metalanguage. In the discussions following the presentations of the papers Landin [1963] links McCarthy’s approach with a paper presented by Strachey at the same conference, which in retrospect may be seen as introducing ideas that led to the concept of denotational semantics [Scott and Strachey, 1971]. (Helpfully for computer science historians, fairly full accounts of these discussions are included in the proceedings.)

8.2 Introspection

The idea of a programming language being able to handle an explicit representation of its own state was of interest to workers in Artificial Intelligence. It was argued that a crucial component of an intelligent system is the ability to reason about itself, or to introspect. Maes [1986] gives a simple introduction to the subject. Introspection is first clearly defined in the language 3-Lisp [Smith, 1984], although features that may be regarded as introspective are provided in a more ad hoc manner in earlier languages. The idea is that facilities exist in the language to take objects that may be assumed to exist at the interpreter or metalanguage level and make them first-class entities in the language level. In McCarthy’s micro-Algol meta-interpreter, for example, the pseudo-variable sn might be considered such an object, as indeed might any part of the state ξ. Object level decisions may be made on the basis of their value and new values for them constructed and put back into the meta-interpreter. The term reification came to be used to refer to taking metalevel objects from the program and making them data, while reflection referred to the reverse process of putting data objects into the program [Friedman and Wand, 1984].

Introspection was promoted not only as a way for allowing programs to reflect on themselves, but also as a way for programmers to tailor the programming language for their own needs, changing the evaluation order, adding traces for debugging and so on. It was particularly influential in object-oriented programming [Maes, 1987]. In Smalltalk [Goldberg and Robson, 1989], the idea that “everything is an object”, including the classes that describe objects and hence the concept of a metaclass as the class which describes classes, means that introspection is a natural part of the language. In Prolog, the system predicate clause is a reification predicate, making an aspect of the program into data, while assert is a reflection predicate. Prolog’s retract may be regarded as a combination of reification and reflection. Even the cut (and more so many proposed variants of it) may be regarded as a stereotyped combination of reification and reflection, giving the program access to the abstract search space of the interpreter and modifying it, though with severe restrictions on the modifications possible. This is the connection between extralogical and metalogical promised earlier.

The key to introspection in programming languages is that it relies on an abstract interpreter, which is in fact a virtual interpreter. There is no requirement that the meta-interpreter, which it assumes exists, actually does exist. Indeed it cannot always, since reflection is often recursive: it is possible for the metalayer to introspect on a meta-metalayer and so on infinitely, producing what is described as the “tower of interpreters”. The real interpreter or compiler, which actually implements the language, is something different and remains unreachable by the programmer. The concepts in the meta-interpreters are produced lazily as required. If introspection were introduced into McCarthy’s Micro-Algol, for instance, it would be possible to access and change sn, the statement position indicator. But there is a clear distinction between this, an abstract concept and the program counter of the underlying assembly language. However deep you got into the tower of meta-interpreters, you would never hit the real program counter.

The problem with introspection in programming languages is that it gives both the power and the danger of self-modifying code. In 3-Lisp it is possible to access and change variable bindings and function definitions using introspection. Thus the barrier between language and metalanguage is broken down, it becomes a notational convenience, but not one that can be relied on. If we can change the environment at whim, we have lost the valuable declarative properties of functional programming.

As Hofstadter [1979] puts it: “below every tangled hierarchy lies an inviolate level.”

The only real metalanguage when we have unlimited reflection (perhaps we could call it the hyper-language) is the inviolate machine code which implements the system. In our constitutional analogy, we are back to the position where the only absolute power lies with the whim of the sovereign; there are no hard constitutional safeguards. The reasonableness of the programmer in not misusing reflection can be compared to the reasonableness of the British middle classes in Bagehot’s defense of the unwritten constitution.