Academia.eduAcademia.edu

Composing Programming Languages by Combining Action-Semantics Modules

2001, Electronic Notes in Theoretical Computer Science

Science of Computer Programming 47 (2003) 3 – 36 www.elsevier.com/locate/scico Composing programming languages by combining action-semantics modules Kyung-Goo Doha;∗;1 , Peter D. Mossesb;2 a Department of Computer Science and Engineering, Hanyang University, Ansan, South Korea and Department of Computer Science, University of Aarhus, Denmark b BRICS Received 22 May 2002; accepted 4 July 2002 Abstract This article demonstrates a method for composing a programming language by combining action-semantics modules. Each module is de ned separately, and then a programming-language module is de ned by combining existing modules. This method enables the language designer to gradually develop a language by de ning, selecting and combining suitable modules. The resulting modular structure is substantially di erent from that previously employed in action-semantic descriptions. It also discusses how to resolve the con icts that may arise when combining modules, and indicates some advantages that action semantics has over other approaches in this respect. c 2002 Elsevier Science B.V. All rights reserved.  Keywords: Action semantics; Modularity; ASF+SDF 1. Introduction Hoare noted in his POPL’73 paper ([4], also in [5]) that much of programming language design is consolidation, not innovation. In other words, a language designer should largely utilize constructions and principles that have worked well in earlier design projects and experiments [19] (cf. the evident close relationship between C and Corresponding author. E-mail addresses: doh@cse.hanyang.ac.kr (K.-G. Doh), pdmosses@brics.dk (P.D. Mosses). 1 This work was supported by grant No. R01-2000-00287 from the Basic Research Program of the Korea Science & Engineering Foundation. 2 Supported by BRICS: Basic Research in Computer Science, Funded by the Danish National Research Foundation. ∗ c 2002 Elsevier Science B.V. All rights reserved. 0167-6423/03/$ - see front matter  PII: S 0 1 6 7 - 6 4 2 3 ( 0 2 ) 0 0 1 0 7 - 7 4 K.-G. Doh, P.D. Mosses / Science of Computer Programming 47 (2003) 3 – 36 C++, C and Java, 1 and Java and C#. 2 ) To this end, language de nitions should be modularized. Then, a language designer’s job may involve the de nition or reuse of many di erent alternative language modules, to choose the best combination of them based on language-design principles, and to reject any that show mutual inconsistencies. Any remaining minor inconsistencies or overlaps may be reconciled by applying good engineering principles. A semantic framework must provide a high degree of modularity so that the designer can build up a language description smoothly and uniformly. For de ning (concrete or abstract) context-free syntax, BNF grammars have excellent modularity: the union of two BNF grammars (without start symbols) is always wellformed, and speci es the language with all the constructs speci ed by the two grammars separately; for de ning semantics, the framework of action semantics appears to o er similar advantages. Action semantics has been developed by Mosses and Watt [11–14,26]. The motto of action semantics is to allow useful semantic description of realistic programming languages [11]. The potential uses of action semantic descriptions include documentation of decisions during language design, language standardization, guidance for implementors, compiler and interpreter generation, and formal reasoning about programs; Mosses [12] gives a survey of some actual uses. In action semantics, a high-level notation called action notation is used to describe the meaning of a programming language. Primitive actions exist for the fundamental behaviours of information processing: value passing, arithmetic, binding creation and lookup, storage allocation and manipulation, and so on. Actions are composed into a combined action with combinators controlling the ow of information. Actions have semi-descriptive English names, which gives a novice reader hints of the intuition behind them. Furthermore, action semantics speci cations are inherently modular. Hence, they can be straightforwardly extended and modi ed to re ect language design changes, and to reuse parts of an existing language de nition for specifying similar, related languages. In fact, some real-world programming languages, such as Pascal [16], and Standard ML [27,28], have been successfully described in action semantics. 1.1. This work This article demonstrates a method for composing a programming language by combining action-semantics modules. There are three kinds of modules: semantic functions modules, which merely declare the names and types of semantic functions (without any semantic equations); semantic equations modules, each of which declares the syntax of, and de nes the semantics of, a single language construct; and semantic entities modules, which declare the standard notation for actions, and de ne further auxiliary sorts and operations. Each module is speci ed separately, and then a complete programming-language module is de ned by properly combining existing modules. This method enables the 1 2 Java is a trademark of Sun Microsystems, Inc. C# is a trademark of Microsoft Corporation. K.-G. Doh, P.D. Mosses / Science of Computer Programming 47 (2003) 3 – 36 5 language designer to gradually develop a language by de ning, selecting, and combining suitable modules. The size of each module is extremely small compared to those previously employed in action-semantic descriptions, and their overall organization is signi cantly di erent: the original style was to specify abstract syntax in one module, and to have a separate module for each syntactic sort, specifying the semantic functions and equations for all the syntactic constructs of that sort together. The novel style of modularization proposed here greatly facilitates the direct reuse of entire modules in action semantics. When combining modules, con icts may occur in the de nition of a combined module. This article suggests how to resolve the problem of con icts, exploiting several key features of the action semantics framework. Modules for constructs stemming from the so-called Landin–Tennent design principles (the principles of abstraction, parameterization, correspondence, and quali cation) [19,21] are particularly uniform, and straightforward to combine. Note that only dynamic semantics is considered in this article, since the main focus here is to illustrate the good modularity and extensibility of action semantics. Static semantics (e.g., type-checking semantics) can be de ned in inference–rule format as in Plotkin’s lecture notes on SOS [17] and in Schmidt’s textbook on typed programming languages [19], or in action notation as in Watt’s work [27,28], becoming an essential part of a language speci cation. 1.2. Road map The rest of the article is organized as follows: Section 2 brie y introduces how to specify action semantics in a speci cation framework called ASF+SDF. Section 3 denes modules necessary to compose a simple expression language. Section 4 presents modules for expression bindings, expression blocks, expression parameters, and expressions with e ects. Section 5 shows how a language module can be composed by combining related modules, and discusses how to resolve con icts between combined modules. Section 6 concludes, also considering some other semantic frameworks that aim to support modularity. 2. Action Semantics Action semantics uses context-free grammars to de ne the structure of abstractsyntax trees, and inductively de ned semantic functions to give semantics to such trees. Each semantic equation is compositional and maps an abstract-syntax tree to an action representing the meaning of the tree. In this section, we brie y review the notation for actions, and then illustrate how to specify action semantics descriptions in ASF+SDF. 2.1. Action notation Actions are semantic entities that represent implementation-independent computational behaviour of programs. The performance of an action, which may be part of an 6 K.-G. Doh, P.D. Mosses / Science of Computer Programming 47 (2003) 3 – 36 enclosing action, either terminates normally (the performance of the enclosing action continues normally); or terminates exceptionally (the enclosing action is skipped until the exception is handled); or fails, corresponding to abandoning the current alternative of a choice (any remaining alternative is tried); or diverges, never terminating (the enclosing action also diverges). The performance of an action processes transient data; it creates and accesses bindings of token to data; it allocates and manipulates primary storage; and it communicates between distributed agents. Actions have various facets. The basic facet processes independently of information, focusing on control ow; the functional facet processes transient information, including actions operating on data; the declarative facet processes scoped information, including actions operating on bindings; the imperative facet processes stable information, including actions operating on storage cells; and the communicative facet processes permanent information, including actions operating on distributed systems of agents. Actions with di erent facets can be freely combined to form multi-faceted actions. So-called yielders are used to inspect the current information (without changing it). Some action notation important to understand the underlying concepts of de ned languages is explained along with the semantic descriptions below. However, some highly suggestive data notations are not explained, for brevity. Note that despite its verbose and casual appearance, action notation is completely formal; a list of all the symbols used in the present paper is provided in an appendix. A full, formal description of action notation can be found in Mosses’s book on action semantics [11], and in a more recent Modular SOS de nition [14]. This paper uses a new version of action notation (AN-2) [7], which has a signi cantly simpler kernel than the original version, but which is otherwise quite similar in use. The di erences between the two versions do not substantially a ect the modularity issues addressed in the present paper. 2.2. Action semantics in ASF+SDF In this paper, we use the ASF+SDF formalism [22,23] to express action semantics descriptions. ASF+SDF di ers somewhat from the meta-notation usually employed in action semantics [11], but it has the advantage that the modules can be checked for well-formedness using the current version of the ASF+SDF Meta-Environment. 3 Moreover, ASF+SDF is a well-established formalism that may already be familiar to many readers. In fact it is the result of the marriage of two formalisms: algebraic speci cation formalism (ASF) and syntax de nition formalism (SDF). ASF is based on the notion of a module declaring a signature (sorts, function symbols, and variables) and giving a set of conditional equations de ning their properties. Modules can be imported into other modules. SDF allows the de nition of concrete (lexical and context-free) syntax for operations and variables. The following overview indicates how action semantics 3 The ASD Tools [24], previously developed to generate ASF+SDF modules from a notation similar to the original meta-notation of action semantics, are incompatible with the new modular structure proposed here. K.-G. Doh, P.D. Mosses / Science of Computer Programming 47 (2003) 3 – 36 7 descriptions are written in ASF+SDF; for further details of ASF+SDF, the reader is referred to [22,23]. With the novel modular structure for action semantics descriptions proposed here, a description is divided into the following kinds of modules: Semantic functions. Each such module declares a single semantic function, say f, for a particular sort of syntax, say s: "f" "[[" s "]]" -> t, where t is some sort of semantic entities—usually the sort Action of all possible actions. It imports the semantic entities modules that are needed to express entities of sort t. The double brackets [[...]] are included as part of the (mix x) notation for each semantic function (they have to be quoted in declarations, but not when they are used). The module also declares variables ranging over s, for instance: "S"[1-9]? -> s declares S; S1; : : : ; S9 as variables. Semantic equations. Such a module declares an abstract syntax constructor operation, and gives a semantic equation de ning the result of applying a particular semantic function to abstract syntax trees constructed by this operation—generally applying other semantic functions to components. Suppose each symbol w1; : : : ; wn is either a quoted symbol "..." or a syntactic sort symbol. Then if s is also a syntactic sort symbol, w1 ... wn -> s declares an abstract syntax constructor with the indicated argument sorts (distinguished from other constructs partly by the quoted symbols). The semantics of all trees thus constructed is speci ed by a semantic equation written: f [[ v1 ... vn ]] = T, where each vi is either an (unquoted) symbol wi or a variable ranging over a sort wi, and T is a term of sort t specifying how the semantics fi[[vi]] of the components are combined. The module imports the semantic functions modules for all the syntactic sorts and semantic functions involved, and any required semantic entities modules that are not already indirectly imported. Semantic entities. These modules may specify new sorts of data with constructors and selectors, include these sorts into previously declared sorts, and de ne derived operations. The standard semantic entities module AN declares all the symbols provided by (the new version of) action notation for expressing actions, data, and yielders. (In fact AN itself has an interesting internal modular structure, but it is irrelevant to our main purpose in the present paper, and not discussed further here.) 8 K.-G. Doh, P.D. Mosses / Science of Computer Programming 47 (2003) 3 – 36 To specify a sort s with constructor operation f and argument sorts s1, . . . , sm, we generally keep to pre x notation and declare the operation thus: f(s1,...,sm) -> s, where s1,...,sm are the argument sorts and s the result sort. For perspicuity we shall also label the component sorts with the names of the corresponding selector operations, e.g.: f(g1:s1,...,gm:sm) -> s. However, such labels do not (presently) give rise to operation declarations in ASF+ SDF, so a separate declaration of each selector (of the form "gi" s -> si, for pre x notation gi(S)) has to be given. Derived operations are de ned straightforwardly by equations. To compose a language, one simply imports the semantic equations modules for all the desired constructs (the relevant semantic functions and semantic entities modules are imported indirectly). Note that di erent semantic equations modules for the same abstract syntax construct should not be imported together: their inconsistency has to be removed rst, as described in Section 5. Concrete illustrations of how action semantic descriptions are formulated in ASF+SDF are given in Sections 3 and 4. The full ASF+SDF sources of all the examples are available from http://www.brics.dk/∼pdm/LDTA-01/. 3. Action-semantics modules for an expression language In this section, we de ne, in ASF+SDF, action-semantics modules that allow us to compose an expression language, as an illustration of the general approach. We start with a semantics entities module named Values as follows: 4 module Values imports AN exports sorts Value context-free syntax Value -> Datum The module Values above imports the prede ned module AN, which speci es the entire action notation (version 2, referred to as AN-2 [7]). Value, a new sort of data, is merely declared to be a subsort of Datum, which is the sort of all individual items of 4 Some lines of SDF, necessary for the implementation but otherwise of no real interest, have occasionally been elided from the examples. K.-G. Doh, P.D. Mosses / Science of Computer Programming 47 (2003) 3 – 36 9 data in action notation; exactly which values are in Value is left to be further speci ed in other modules. Next, a semantic functions module Exp for expressions is speci ed as follows: module Exp imports Values exports sorts Exp context-free syntax "evaluate" "[[" Exp "]]" -> Action %% giving Value variables "E"[1-9]? -> Exp In action semantics, a semantic function maps an abstract-syntax tree to an action representing its computational meaning. The module Exp declares the semantic function evaluate and its functionality, leaving its meaning to be de ned by semantic equations modules. The line declaring evaluate indicates that for every abstract-syntax tree E in the abstract-syntax domain Exp, the semantic entity evaluate [[E]] is an action. The comment at the end of the line (starting %%) indicates that evaluate [[E]] gives a result of sort Value (on normal termination), i.e. Value is the sort of expressible values. 5 The last line of the module declares variables ranging over Exp, for use in semantic equations modules that involve expressions. The following module declares a semantic function for binary operators: module Op2 imports Values exports sorts Op2 context-free syntax "operate2" "[[" Op2 "]]" -> Action %% taking (Value,Value) %% giving Value variables "O2" -> Op2 The following module de nes the syntax and semantics of expressions with binary operators. Note again that the speci cs of what kinds of binary operators may be used is left to be speci ed separately. module Exp/binary imports Exp Op2 exports context-free syntax Exp Op2 Exp -> Exp equations 5 The AN-2 notation for formally specifying such properties of actions has not yet been settled. 10 K.-G. Doh, P.D. Mosses / Science of Computer Programming 47 (2003) 3 – 36 [1] evaluate [[ E1 O2 E2 ]] = (evaluate [[E1]] and evaluate [[E2]]) then operate2 [[O2]] The notation Exp Op2 Exp -> Exp above is equivalent to the usual BNF notation Exp ::= Exp Op2 Exp. The variables used in the above module are declared in the modules Exp and Op2. The meaning of evaluate [[ E1 O2 E2 ]] can be explained informally as follows: both E1 and E2 are evaluated to give values, and then the values are passed to be operated upon by O2. Notice how close this informal explanation is to Equation [1] of the module Exp/binary above (which is completely formal, despite its “natural” appearance). As the reader might have noticed, the semantic equations in action semantics are compositional, as in Scott–Strachey style denotational semantics [18,20]: the semantics of any compound phrase is determined in terms of the semantics of its subphrases. The action combinator and is a basic combinator that represents implementationdependent order of performance; when there is no interference between the two subactions of and, the order of evaluation is not signi cant. The and combinator makes a tuple of transient values given by its sub-actions. The then combinator passes transient values from the left sub-action to the right sub-action. Each speci c binary operator can be de ned in a separate module as follows: module Op2/plus imports Op2 exports context-free syntax "+" -> Op2 Int -> Value equations [2] operate2 [[ + ]] = give (the int #1 + the int #2) The inclusion of Int in Value is clearly needed when integer addition is allowed in expressions, so it is speci ed above. (An alternative style would be to defer all speci cations of sort inclusions until after the composition of a language has been decided.) Lowercase constant symbols are used in action notation to indicate sorts of data, e.g. int indicates the sort Int. When ds indicates any sort of data, the data operation the ds projects its argument onto that sort, the result being unde ned if the argument is not in the sort. For any (positive) numeral n, the data operation #n selects the nth component from any data tuple having at least that many components. The composition the ds #n of these data operations is a yielder that rst selects the nth component then checks that it is of the sort indicated by ds. Hence above, the int #1 + the int #2 yields the sum of the rst two components of the given data tuple, provided that they are both of sort Int. For any yielder Y , the action give Y simply gives the value yielded by Y (an item of data is a special case of a yielder). K.-G. Doh, P.D. Mosses / Science of Computer Programming 47 (2003) 3 – 36 11 Expression grouping can be speci ed as follows: module Exp/group imports Exp exports context-free syntax "(" Exp ")" -> Exp {bracket} By declaring the syntax above as a bracketing construct, no node is generated for it in the abstract syntax tree, so no semantic equation for evaluate [[(E)]] is needed. The module Lit below declares value to be a semantic function for the syntactic sort Lit of literal constants. The semantics of literal constants is assumed to be not only inherently “mathematical” but also independent of the context, and it is taken to be an item of data (rather than an action). Notice that no assumption is made here about whether the sort Lit is included in Exp. module Lit imports AN exports sorts Lit LitValue context-free syntax "value" "[[" Lit "]]" -> LitValue LitValue -> Datum variables "L"[1-9]? -> Lit Shown below is a semantic equations module which speci es the syntax and semantics of literal constants in expressions. Exactly what constitutes the literal constants is left unspeci ed. module Exp/literal imports Exp Lit exports context-free syntax Lit -> Exp LitValue -> Value equations [3] evaluate [[ L ]] = give value [[L]] The notation Lit -> Exp above speci es the inclusion of the abstract syntax of Lit in Exp, and corresponds to the usual BNF notation Exp ::= Lit. Equation [3] above speci es how the values of literal constants contribute to the actions that evaluate expressions. Note that what kinds of literals there might be is still left unspeci ed in the above module. Some speci c literals, such as numeric constants, can be speci ed in separate modules. 12 K.-G. Doh, P.D. Mosses / Science of Computer Programming 47 (2003) 3 – 36 The sort Nat of natural numbers used in the following module is provided by AN, together with ordinary decimal notation. The following semantic equations merely conrm that the semantics of a numeric literal is indeed decimal. 6 module Lit/numeric imports Lit exports sorts Num context-free syntax Num -> Lit Nat -> LitValue lexical syntax [0-9]+ -> Num hiddens variables "C+" -> CHAR+ "C" -> CHAR equations [4-0] value [[ num("0") ]] = 0 %% ... [4-9] value [[ num("9") ]] = 9 [4-10] value [[ num(C+ C) ]] = (10 * value [[num(C+)]]) + value [[num(C)]] The variables C and C+ are declared as hidden above since the module is essentially declaring both the syntax and semantics for Num. The next module departs slightly from the strictly minimalist style proposed in this paper, by specifying the semantics of two di erent constructs together. In general, it seems best to stick to specifying all constructs separately, although here, it indeed seems quite unlikely that a language would include true without false, or vice versa. module Lit/boolean imports Lit exports context-free syntax "true" -> Lit "false" -> Lit Bool -> LitValue equations [5] value [[ true ]] = true [6] value [[ false ]] = false 6 In fact it is not necessary to be so pedantic: an alternative is to include Nat in the syntax of literals, and let value[[N]] = N. K.-G. Doh, P.D. Mosses / Science of Computer Programming 47 (2003) 3 – 36 13 By the way, the Bool values true and false used on the right of the above equations are provided by the standard action notation (which is imported indirectly, via the module Lit). By now, the reader should have become reasonably familiar with our use of ASF+SDF to specify semantic functions and semantic equations modules. Some further examples of modules specifying semantic entities will be provided in Section 4. As a further example, relational expressions and conditional expressions can be similarly speci ed by de ning modules as follows: module Op1 imports Values exports sorts Op1 context-free syntax "operate1" "[[" Op1 "]]" -> Action %% taking Value %% giving Value variables "O1" -> Op1 module Op1/not imports Op1 exports context-free syntax "not" -> Op1 Bool -> Value equations [7] operate1 [[ not ]] = give (not the bool) module Op2/equal imports Op2 exports context-free syntax "=" -> Op2 Bool -> Value equations [8] operate2 [[ = ]] = when (the value #1 = the value #2) then give true otherwise give false module Exp/unary imports Exp Op1 exports context-free syntax Op1 Exp -> Exp equations [9] evaluate [[ O1 E ]] = evaluate [[E]] then operate1 [[O1]] 14 K.-G. Doh, P.D. Mosses / Science of Computer Programming 47 (2003) 3 – 36 ExpressionLanguage Exp/ group Exp/ if-then-else Exp Exp/ unary Exp/ binary Op1/ not Exp/ literal Op1 Op2/ plus Op2/ minus Op2/ times Op2 Op2/ equal Lit/ numeric Lit/ boolean Lit Values AN Fig. 1. The module dependency graph for ExpressionLanguage. module Exp/if-then-else imports Exp exports context-free syntax "if" Exp "then" Exp "else" Exp -> Exp Bool -> Value equations [10] evaluate [[ if E1 then E2 else E3 ]] = evaluate [[E1]] then select( (given true then evaluate [[E2]]) or (given false then evaluate [[E3]])) Notice that the action combinator or above, generally used for nondeterministic choice, here represents a deterministic choice, since at least one of two sub-actions must always fail. Modules can be combined to specify a programming language. For example, the modules de ned so far can be combined to de ne an expression language as follows: module ExpressionLanguage imports Exp/literal Exp/unary Exp/binary Exp/group Exp/if-then-else Lit/numeric Lit/boolean Op1/not Op2/equal Op2/plus Op2/minus Op2/times Note that shared notation is the crucial feature when combining modules: their internal structure and import relationship are irrelevant. The dependency relationships among imported modules in ExpressionLanguage are depicted as a graph in Fig. 1. The K.-G. Doh, P.D. Mosses / Science of Computer Programming 47 (2003) 3 – 36 15 graph shown is the exact copy of the one generated by the Meta-Environment, with a minor layout modi cation to t in a page. 4. Modules as building blocks for programming languages In this section, we de ne modules for expression bindings, expression blocks, expression parameters, and expressions with e ects, in turn. On the way, we contemplate the semantics of eager vs. lazy binding, static vs. dynamic scoping, call-by-value vs. call-by-name parameter passing schemes, and expressions with e ects. 4.1. Modules for expression bindings According to the Landin–Tennent programming-language design principles, the phrases in any semantically meaningful syntactic class may be named, which is specifically called the abstraction principle. The process of giving a name to a program construct is called binding. A named expression is called an expression abstraction, which can be invoked later by simply mentioning its name. The semantic functions module Ide for names (identi ers) is as follows: module Ide imports AN exports sorts Ide context-free syntax "token" "[[" Ide "]]" -> Token variables "I"[1-9]? -> Ide The symbol Ide in the body of the above module is a syntactic sort for names, whereas Token, imported from AN, is the sort of all data items that may be bound to values in actions. The semantic function module Dec for declarations (binding constructs) is speci ed as follows: module Dec imports AN exports sorts Dec context-free syntax "declare" "[[" Dec "]]" -> Action %% giving Bindings variables "D"[1-9]? -> Dec The constructs for declarations are grouped into a separate syntax sort, Dec, the details of which are yet to be speci ed. The functionality of the semantic function declare indicates that for every abstract-syntax tree D in the syntactic sort Dec, the semantic entity declare [[D]] is an action which gives bindings. 16 K.-G. Doh, P.D. Mosses / Science of Computer Programming 47 (2003) 3 – 36 The meaning of an expression abstraction may be di erent depending on when the expression is evaluated. An expression may be evaluated before it is bound to a name (called eager binding), or after it is invoked (called lazy binding). 4.1.1. Eager binding The semantic equations module for expression-binding with eager evaluation, Dec/val, is speci ed as follows: module Dec/val imports Dec Ide Exp exports context-free syntax "val" Ide "=" Exp -> Dec equations [11] declare [[ val I = E ]] = evaluate [[E]] then give binding (token [[I]], the bindable) The semantic equation [11] above shows that the body of an expression binding, E, is evaluated before being bound to a token, implying eager evaluation. The sort of all values that can be bound to tokens in actions is called Bindable, and is yet to be speci ed. In the case of eager binding, the Landin–Tennent principle motivates identifying Bindable with Value, or at least letting it include all values; but by leaving the relationship between Bindable and Value open, the above module may be useful also for composing languages whose design does not dogmatically adhere to the mentioned principle. Note that when not all values are bindable, the above action may fail. The semantic equation module Exp/const-val for name invocation is de ned as follows: module Exp/const-val imports Exp Ide exports context-free syntax Ide -> Exp equations [12] evaluate [[ I ]] = give the value bound to token [[I]] The name invocation looks up current bindings and gives the value bound to the name. Here, the action may fail when not all bindables are values—just the opposite of the situation with the module Dec/val. 4.1.2. Lazy binding In lazy binding, an expression is not evaluated and is bound to a name. The semantic entities module Functions introduces a new sort, Function, representing a function K.-G. Doh, P.D. Mosses / Science of Computer Programming 47 (2003) 3 – 36 17 abstraction. It also speci es a new action apply which takes a tuple of an action and a datum and performs the action with the given datum. The primitive action enact simply performs the action given to it as data (Action is actually a subsort of Data). module Functions imports AN exports sorts Function context-free syntax Function -> Datum apply -> Action %% taking (Action,Datum) equations [13] apply = enact (provide the datum#2 then the action#1) The next semantic entities module is de ned speci cally for nullary functions. A nullary function is constructed from an action that corresponds to an unevaluated expression, and the action can be retrieved by applying the selector action to it: module Functions/nullary imports Functions exports context-free syntax nullary-function(action:Action) -> Function The semantic functions module for expression binding with lazy evaluation, Dec/fun-nullary, is now de ned as follows: module Dec/fun-nullary imports Dec Ide Exp Functions/nullary exports context-free syntax "fun" Ide "=" Exp -> Dec Function -> Bindable equations [14] declare [[ fun I = E ]] = give binding (token [[I]], nullary-function (closure (evaluate [[E]]))) The yielder nullary-function A, where A is an action, yields a datum encapsulating A, but keeping no bindings. In order to keep the bindings that are current at the evaluation of nullary-function A, closure must be used: closure lets the encapsulated action keep the current bindings, and then when the encapsulated action is performed, it receives the kept bindings. Thus, nullary-function (closure (evaluate [[E]])) yields an encapsulated action of evaluate E, but the action incorporates the bindings available at the time, i.e., at declaration-time. Thus, the action evaluate [[E]] in Equation [14] above is not performed, but encapsulated with the 18 K.-G. Doh, P.D. Mosses / Science of Computer Programming 47 (2003) 3 – 36 current bindings and bound to a name (i.e., the evaluation is delayed). Since a nullary function is bound to a name, Function is speci ed to be included in Bindable. The semantic equations module Exp/fun-nullary for lazy invocation is de ned as follows: module Exp/fun-nullary imports Exp Ide Functions/nullary exports context-free syntax Ide -> Exp Function -> Bindable equations [15] evaluate [[ I ]] = enact action(the function bound to token [[I]]) The encapsulated action (function) is enacted and performed, receiving the bindings incorporated at declaration-time, when the name is invoked as shown in Equation [15] above. Since the encapsulated action receives the same bindings regardless of where it is enacted and performed, it is called static binding. 4.1.3. Dynamic binding The semantic equations modules for expression binding with lazy evaluation and dynamic binding are de ned as follows: module Dec/fun-nullary-dynamic imports Dec Ide Exp Functions/nullary exports context-free syntax "fun" Ide "=" Exp -> Dec Function -> Bindable equations [16] declare [[ fun I = E ]] = give binding (token [[I]], nullary-function (evaluate [[E]])) module Exp/fun-nullary-dynamic imports Exp Ide Functions/nullary exports context-free syntax Ide -> Exp Function -> Bindable equations [17] evaluate [[ I ]] = enact (closure action(the function bound to token [[I]])) K.-G. Doh, P.D. Mosses / Science of Computer Programming 47 (2003) 3 – 36 19 The yielder closure action(the function bound to token [[I]]) yields an action that incorporates the bindings available at the time, i.e., at invocation-time. Thus the enaction performs the action encapsulated in the action, letting it receive the current bindings. (The primitive action enact does not itself add any further bindings to those already incorporated in the action given to it.) 4.1.4. Modules for multiple declarations The semantic equations module for multiple, independent declarations is de ned as follows: module Dec/seq-indep imports Dec exports context-free syntax Dec "," Dec -> Dec equations [18] declare [[ D1 , D2 ]] = declare [[D1]] and then declare [[D2]] then give disjoint union The action combinator and then indicates that the declarations are declared sequentially, and then the disjoint union of the produced bindings is formed (with exceptional termination when both declarations produce a binding for the same token). The module for multiple, sequential declarations is de ned as follows: module Dec/seq imports Dec exports context-free syntax Dec ";" Dec -> Dec equations [19] declare [[ D1 ; D2 ]] = declare [[D1]] before declare [[D2]] The action combinator before indicates that the bindings created by declare [[D1]] can be used in declare[[D2]], and the bindings created by declare [[D2]] overrides the bindings created by declare [[D1]]. 4.2. Modules for expression blocks The Landin–Tennent quali cation principle says that any semantically meaningful syntactic class may admit local declarations. This means in particular that any expression belonging to the syntax class Exp can have local de nitions. A construct admitting local de nitions is called a block. The semantic equations module for an expression 20 K.-G. Doh, P.D. Mosses / Science of Computer Programming 47 (2003) 3 – 36 block is de ned as follows: 7 module Exp/let imports Exp Dec exports context-free syntax "let" Dec "in" Exp -> Exp equations [20] evaluate [[ let D in E ]] = furthermore declare [[D]] hence evaluate [[E]] The action furthermore declare [[D]] overlays the current bindings with the ones produced by declare [[D]]. Then the hence combinator passes the overlaid bindings to the action evaluate [[E]]. This corresponds exactly to an ordinary block structure. Thus, bindings declared in D can only be referred in the body E, not outside the block. The distinction between static and dynamic binding arises when an abstraction created in the scope of one set of bindings may get applied in the scope of another set. In the examples given above, let blocks could involve both static and dynamic scopes for expression abstractions: it is the semantics of the abstractions themselves that determines whether or not the abstraction-time bindings get encapsulated together with the action representing the expression evaluation, for later use; and it is the semantics of name lookup that determines whether or not the lookup-time bindings are made available to the encapsulated action. When an abstraction has determined that it is using the static bindings, it simply ignores any lookup-time bindings supplied by the semantics of name reference. On the other hand, when the semantics of the abstraction ignores the abstraction-time bindings, it depends on the semantics of name lookup as to whether the dynamic bindings will be supplied or not. 4.3. Modules for expression parameters The Landin–Tennent parameterization principle says that phrases from any semantically meaningful syntactic class may be parameters. In particular, any expression belonging to the syntax class Exp can be a parameter. In this subsection, we de ne modules for an expression abstraction with an expression parameter, along with an invocation construct. Modules dealing with declaration abstractions and declaration parameters may be speci ed analogously. The meaning of an abstraction invocation with an actual parameter can be varied depending on when its parameter (argument) is evaluated. Here we examine the semantics of two di erent parameter-passing schemes: call-by-value and call-by-name. 7 In future versions of ASF+SDF, it should be possible to specify a single parameterized module for arbitrary blocks, and instantiate it to get the desired expression blocks. K.-G. Doh, P.D. Mosses / Science of Computer Programming 47 (2003) 3 – 36 21 4.3.1. Call-by-value parameter-passing In the call-by-value parameter-passing scheme, an argument is fully evaluated at the time of invocation of a parameterized abstraction. Then the evaluated value is bound to a formal parameter. The following semantic entities module is de ned speci cally for call-by-value unary functions. module Functions/unary-value imports Functions exports sorts Unary-Value-Function context-free syntax unary-value-function(action:Action) -> Unary-Value-Function Unary-Value-Function -> Function The semantic equations modules for call-by-value parameter passing are de ned as follows: module Dec/fun-unary-value imports Dec Ide Exp Functions/unary-value exports context-free syntax "fun" Ide "(" "val" Ide ")" "=" Exp -> Dec Unary-Value-Function -> Bindable equations [21] declare [[ fun I1 (val I2) = E ]] = give binding (token [[I1]], unary-value-function ( closure (furthermore bind (token [[I2]], the value) hence evaluate [[E]]))) module Exp/fun-unary-value imports Exp Ide Functions/unary-value exports context-free syntax Ide "(" Exp ")" -> Exp Unary-Value-Function -> Bindable equations [22] evaluate [[ I (E) ]] = (give the unary-value-function bound to token [[I]] and then evaluate [[E]]) then apply (action(the function#1), the value#2) Equation [22] in the module Exp/fun-unary-value shows that an argument E of function application is evaluated to a value, and then the unary function bound to a 22 K.-G. Doh, P.D. Mosses / Science of Computer Programming 47 (2003) 3 – 36 token I is applied to the value, so that the evaluated argument value is bound to a formal parameter. The binding acts as a local declaration in the body of the function. Equation [21] in the module Dec/fun-unary-value indicates that the bindings current at the de nition are used when the unary function is applied, implying static scoping. Note that the module Exp/const-val in Section 4.1.1 (rather than Exp/fun-nullary in Section 4.1.2) is combined smoothly together with these modules, which shows that eager binding and call-by-value scheme t well together. 4.3.2. Call-by-name parameter-passing In the call-by-name parameter-passing scheme, the evaluation of an argument is delayed until it is used in the body of the called function. That is, the unevaluated argument is bound to a formal parameter, and then evaluated every time the formal parameter is invoked in the body of the called function. The following semantic entities module is de ned speci cally for call-by-name unary functions. module Functions/unary-name imports Functions exports sorts Unary-Name-Function context-free syntax unary-name-function(action:Action) -> Unary-Name-Function Unary-Name-Function -> Function The semantic equations modules for call-by-name parameter passing are de ned as follows: module Dec/fun-unary-name imports Dec Ide Exp Functions/unary-name exports context-free syntax "fun" Ide "(" "name" Ide ")" "=" Exp -> Dec Unary-Name-Function -> Bindable equations [23] declare [[ fun I1 (name I2) = E ]] = give binding (token [[I1]], unary-name-function ( closure (furthermore bind (token [[I2]], the action) hence evaluate [[E]]))) module Exp/fun-unary-name imports Exp Ide Functions/unary-name exports K.-G. Doh, P.D. Mosses / Science of Computer Programming 47 (2003) 3 – 36 23 context-free syntax Ide "(" Exp ")" -> Exp Unary-Name-Function -> Bindable equations [24] evaluate [[ I (E) ]] = (give the unary-name-function bound to token [[I]] and give closure (evaluate [[E]])) then apply (action(the function#1), the action#2) Equation [24] in the module Exp/fun-unary-name shows that an argument E of function application is not evaluated (i.e., the corresponding action evaluate E is encapsulated), and then the unary function is applied to the unevaluated argument, with the result that the encapsulated action representing the unevaluated argument is bound to a formal parameter as shown in the module Dec/fun-unary-name. The closure in Equation [24] in the module Exp/fun-unary-name suggests that the bindings available when the parameterized abstraction is invoked should be used when the argument is invoked and evaluated in the body of parameterized abstraction, implying static binding. Since the action representing unevaluated argument is enacted in the body of parameterized function, the module Exp/name-val below (instead of Exp/const-val in Section 4.1.1) should be used with these modules. As a result, we can see that lazy binding and call-by-name t well together. module Exp/name-val imports Exp Ide exports context-free syntax Ide -> Exp Action -> Bindable equations [25] evaluate [[ I ]] = enact the action bound to token [[I]] 4.4. Modules for expressions with e ects In this section, we examine modules for expressions with e ects. We rst show the semantic entities module Variables introducing a new sort Variable and two new actions, assign and dereference, necessary to de ne expression modules with e ects. module Variables imports AN exports sorts Variable context-free syntax Variable -> Datum 24 K.-G. Doh, P.D. Mosses / Science of Computer Programming 47 (2003) 3 – 36 "assign" -> Action %% %% "dereference" -> Action %% %% taking giving taking giving (Variable,Value) () Variable Value We then de ne the semantic entities module Variables/simple specifying the meanings of the actions, assign and dereference, for simple variables. module Variables/simple imports Variables exports sorts Simple-Variable context-free syntax "simple-variable"(cell:Cell) -> Simple-Variable Simple-Variable -> Variable equations [26] assign = given (the simple-variable#1, the storable#2) then update (cell(the simple-variable#1), the storable#2) [27] dereference = given the simple-variable then inspect (cell(the simple-variable)) We next de ne semantic equations modules for expressions with e ects, with an ML-like syntax. Equation [28] in the module Exp/new-var-init below states the meaning of evaluating ref E is: the expression E evaluates to a value, a new cell is allocated, the evaluated value is stored in the cell, and then the allocated cell is returned as a variable. module Exp/new-var-init imports Exp Variables/simple exports context-free syntax "ref" Exp -> Exp Simple-Variable -> Value equations [28] evaluate [[ ref E ]] = evaluate [[E]] then create then give simple-variable(the cell) Equation [29] in the module Exp/var-val below indicates that the meaning of evaluating ! E is: the expression E evaluates to a variable and the value stored in the variable is returned. Notice that this module would remain unchanged if compound variables are introduced, provided that the de nition of dereference is extended appropriately (which can be done by adding new modules, leaving those given above unchanged). K.-G. Doh, P.D. Mosses / Science of Computer Programming 47 (2003) 3 – 36 25 module Exp/var-val imports Exp Variables exports context-free syntax "!" Exp -> Exp Variable -> Value equations [29] evaluate [[ ! E ]] = evaluate [[E]] then dereference the variable Equation [30] in the module Exp/assign below de nes the meaning of evaluating E1 := E2 as: an expression E1 evaluates to a variable and an expression E2 evaluates to a value; then the value is assigned to the variable. As with dereferencing, the use of assign allows the modules to remain unchanged if compound variables are introduced. module Exp/assign imports Exp Variables exports context-free syntax Exp ":=" Exp -> Exp Variable -> Value equations [30] evaluate [[ E1 := E2 ]] = (evaluate [[E1]] and evaluate [[E2]]) then assign (the variable#1, the value#2) Note that the values are now extended to include variables. Finally, the semantic equations module for expression sequencing is de ned as follows: module Exp/seq imports Exp exports context-free syntax Exp ";" Exp -> Exp equations [31] evaluate [[ E1 ; E2 ]] = evaluate [[E1]] then skip then evaluate [[E2]] The action combinator then in Equation [31] above forces its sub-actions to be performed sequentially. The basic action skip discards the value given by evaluate [[E1]]. In this section, we have built modules for various language constructs: expression bindings, expression blocks, expression parameters and expressions with e ects. One could also de ne declaration bindings, declaration blocks, and declaration parameters in a similar fashion, but the presentation would not illustrate any new points of interest, so we do not bother with that here. 26 K.-G. Doh, P.D. Mosses / Science of Computer Programming 47 (2003) 3 – 36 5. Combining modules The modules developed in the previous sections can be combined to become complete programming-language modules, as indicated by the module Expression Language in Section 3. Combining modules can be achieved simply by importing them together into another module, assuming that the symbols they share correspond to common features. This is because the actions in the semantic equations remain well-formed (and meaningful), regardless of how rich the denotations of component constructs become (e.g. when purely functional expressions are enriched with sidee ects). However, we need to consider what should happen when the modules to be combined specify di erent sort inclusions involving the same sorts, or di erent semantic equations for the same syntactic constructs. The only problem with sort inclusions is when both S1 -> S2 and S2 -> S1 occur together in a combined module (directly or indirectly): they imply that the sorts are identical, including exactly the same values. In ASF+SDF, it appears that such mutual inclusions do not a ect parsing, but one might alternatively make the sorts into socalled aliases. When there are two di erent semantic equations for the same syntactic construct in the modules to be combined, we say that there is a con ict. In fact, some combinations of the modules speci ed in Section 4 do result in con icts. Such con icts may be resolved automatically by unifying the actions in con icting semantic equations, using (tentatively A1) otherwise A2 to form a choice between them. To avoid patently undesirable combinations, however, we require the resulting action to be equivalent to the combination in the opposite order. 8 For instance, let us try to combine the two modules Exp/constval and Exp/ fun-nullary. Then we have a con ict between the following two equations: evaluate [[ I ]] = give the value bound to token [[I]] evaluate [[ I ]] = enact action(the function bound to token [[I]]) The two actions can be uni ed provided that the semantic entities of sort value do not include any of sort function: evaluate [[ I ]] = (tentatively give the value bound to token [[I]]) otherwise enact action(the function bound to token [[I]]) where the action combination (tentatively A1) otherwise A2 performs A1, and skips A2 if A1 terminates normally, but performs A2 (with the same data as A1) in the case that A1 terminates exceptionally. 9 The action in the new equation above means that if the datum bound to I is of sort value, then the expression evaluation must simply give that value; if the datum 8 9 Action equivalence is undecidable, but we may use any safe decidable approximation to it. tentatively A fails when A terminates exceptionally with no data. K.-G. Doh, P.D. Mosses / Science of Computer Programming 47 (2003) 3 – 36 27 is of sort function, then the evaluation must enact the action; and if it is neither a value nor a function, the evaluation must terminate exceptionally. Since the given datum cannot be simultaneously of sort value and of sort function, the order of combination is insigni cant, as required. Such uni cation of actions permits the modular composition of the languages that, for example, use the same syntax for applying parameterless functions and for referring to ordinary constant values, provided that parameterless functions themselves are not regarded as values. Note, however, that if modules do need to use values of sort function in representing expressible values, the function abstractions there should always be embedded into other sorts by use of distinct constructor functions, to allow subsequent combination with modules such as Exp/fun-nullary. Safest of all is to follow the usual practice in action-semantic descriptions, and always embed entities such as function abstractions in distinct abstraction sorts when using them as (expressible, bindable, or storable) values. For example, a constructor for parameterized functions could embed the action in an abstraction sort Function, which could then be included in Value, whereas a sort Thunk embedding the values of parameterless expression abstractions would be excluded from Value. It appears that Dec/fun-nullary and Dec/fun-nullary-dynamic cannot be unied since, as the following equation shows, the order of combination is signi cant: declare [[ fun I = E ]] = (tentatively give binding (token [[I]], nullary-function closure (evaluate [[E]]))) otherwise give binding (token [[I]], nullary-function (evaluate [[E]])) When two modules cannot be uni ed, we say that they are inconsistent. Let us look at some modules with no con ict. A rst-order lazy functional language supporting static scoping is de ned by including modules as follows: module FirstOrderLazyFunctionalLanguage imports ExpressionLanguage Dec/fun-nullary Exp/fun-nullary Dec/seq-indep Exp/let Dec/fun-unary-name Exp/fun-unary-name Exp/name-val The dependency relationships among imported modules in FirstOrderLazyFunctionalLanguage are shown in Fig. 2. Since there are no con icting semantic equations in the combined modules above, we say that the modules are safely combined. Notice that although Exp/fun-nullary imports only Exp, which does not itself specify the syntax or semantics of any particular expression constructs, the e ect of the combination is just as if all the modules for expression constructs had also been imported. 28 K.-G. Doh, P.D. Mosses / Science of Computer Programming 47 (2003) 3 – 36 ExpressionLanguage FirstOrderLazyFunctionalLanguage Dec/ seq-indep Exp/ let Dec/ fun-nullary Functions/ nullary Exp/ fun-nullary Dec/ fun-unary-name Exp/ fun-unary-name Exp/ name-val Functions/ unary-name Dec Ide Functions Exp Values AN Fig. 2. The module dependency graph for FirstOrderLazyFunctionalLanguage. As an another example, let us look at the module for a rst-order eager functional language with e ects: module FirstOrderEagerFunctionalLanguageWithEffects imports ExpressionLanguage Dec/val Exp/const-val Dec/seq Exp/let Dec/fun-unary-value Exp/fun-unary-value Exp/new-var-init Exp/var-val Exp/assign Exp/seq exports context-free syntax Simple-Variable -> Bindable Int -> Storable The dependency relationships among imported modules in FirstOrderEagerFunctionalLanguageWithEffects are shown in Fig. 3. The combination in the above module presents no con icting semantic equations. However, since the sorts Bindable and Storable have remained unspeci ed in the imported modules, they need to be fully speci ed in the combined module in order for the language to be complete. This means that the decision about which sorts of values should be bindable and=or storable has been left open until we form a complete language. In fact, we might choose them minimally, to be no more than required in the above combined 29 K.-G. Doh, P.D. Mosses / Science of Computer Programming 47 (2003) 3 – 36 ExpressionLanguage FirstOrderEagerFunctionalLanguageWithEffect Exp/ Exp/ Exp/ new-var-init var-val assign Exp/ seq Exp/ const-val Exp/ fun-unary-value Dec/ val Exp/ let Dec/ Dec/ fun-unary-value seq Functions/ unary-value Variables/ simple Exp Variables Ide Dec Functions Value AN Fig. 3. The module dependency graph for FirstOrderEagerFunctionalLanguageWithEffects. module; at the other extreme, we might choose them as follows: Simple-Variable | Int | Bool -> Bindable Simple-Variable | Int | Bool -> Storable Note that in the meanwhile Value in the combined module must include at least the following sorts: Simple-Variable | Int | Bool -> Value The observant reader may have noticed that the combination of the ExpressionLanguage module with the modules, Exp/new-var-init, Exp/var-val and Exp/assign, has undermined the determinism of the described language: assignments occurring in sub-expressions of the same arithmetic expression may be interleaved in any order, and this clearly may lead to di erent possible values of expressions. Such nondeterminism is quite di erent from that arising when unifying actions, since it does not involve a choice between performing di erent actions, and merely re ects the possibility of observing an internal nondeterminism that was already present in the computational semantics of actions representing expression evaluation; its appearance should therefore not invalidate module combination. Incidentally, even if one does regard nondeterminism due to interleaving as undesirable, and perhaps wishes to prohibit just those programs whose outcome may depend on which interleaving is chosen, the nondeterministic semantics is still needed, in order to distinguish the prohibited programs from the others. We have illustrated our approach by combining action-semantics modules based on expression-based constructs taken from functional programming languages. It is, of 30 K.-G. Doh, P.D. Mosses / Science of Computer Programming 47 (2003) 3 – 36 course, equally possible to develop imperative languages by de ning a language module for conventional imperative constructs, with a new syntax domain Command including assignment, loop and sequencing constructs, and then de ning and combining modules as we have done in this paper. With the modular structure previously adopted in action semantics, each module typically de nes a semantic function on an entire syntactic sort. Thus a conventional action-semantic description of the rst-order language with e ects would have essentially just two modules de ning semantic functions: one for evaluating expressions, the other for declaring de nitions. The inherent modularity of action notation allows reuse of individual semantic equations in other descriptions, but it is unlikely that such large modules would ever be reused in toto, merely by referring to them. In contrast, the much ner modular structure proposed in this paper should encourage the direct reuse of modules from one action-semantic description in later ones. Note however that although the direct use of ASF+SDF as shown in this paper is quite convenient and perspicuous, it would probably be advantageous to generate the desired ASF+SDF modules from a more concise meta-notation (e.g. eliminating lengthy keywords such as context-free syntax), as in the ASD Tools developed by van Deursen and Mosses [24]. Finally, let us note that we have here focused on the modules de ning semantic equations. In large-scale action-semantic descriptions, also the speci cation of semantic entities (providing data types and action abbreviations for higher-level concepts, such as compound variables and communication protocols) has an interesting modular structure. When the modules de ning semantic equations are structured so as to group related constructs together, the modules de ning semantic entities may be more easily localized, making it apparent that they are only needed in connection with the semantics of particular language constructs. However, an appropriate visualization of the import relationship between modules may be as e ective as (and more exible than) an explicit indication of the intended grouping of modules for semantic entities with those for semantic equations. 6. Conclusion We have demonstrated how to use action semantics to de ne and combine language modules. The good modularity and extensibility of action semantics help us systematically develop programming languages. Particularly in the presence of the language modules for similar languages, new language modules can be de ned with only a few modi cations when using the ne-grained modular structure proposed here. The ideas developed in this article can be adopted to guide the design of domainspeci c languages and=or rapidly prototyped special-purpose languages. When one designs such a language, one can analyze its problem domain, design a core language, and then gradually build up language features to it according to some rational design principles, such as the Landin–Tennent principles. It should also be possible to reuse modules in the style of those shown in this paper when describing previously designed languages; in a future paper, we intend to report K.-G. Doh, P.D. Mosses / Science of Computer Programming 47 (2003) 3 – 36 31 on a case-study involving the action-semantic description of an existing domain-speci c language. Good tool support [3] is clearly essential for checking the well-formedness and consistency of modules (and ultimately, for generating prototype implementations from them). It appears that use of ASF+SDF and the Meta-Environment [23] is a good basis for such tool support. The implementation of tools should be facilitated by our adoption of the much-simpli ed version of action notation that was proposed by Lassen, Mosses, and Watt at an Action Semantics Workshop [7]. Other semantic frameworks that aim to provide a high degree of modularity have been developed. For example, Moggi [10] has proposed the use of monads and monad transformers in denotational semantics. Cartwright and Felleisen [1] have proposed the use of an operationally motivated style of denotational semantics, in the interests of modularity; it uses auxiliary notation similar to that of Moggi, but appears to be not so general (nondeterminism and concurrency are excluded). Liang and Hudak have implemented Moggi’s monad transformers [10] in their modular monadic semantics framework [8]. Wansbrough and Hamer have used the modular monadic framework to give a modular monadic semantics of action notation, and called it modular monadic action semantics [25]. In response to such work, Mosses has developed Modular SOS [15] which provides signi cantly greater modularity in SOS, and he has rede ned action notation in Modular SOS, improving dramatically the modularity of the de nition [14]. The abstract state machine (ASM) approach [2] also provides modularity for operational semantics, through the use of a particularly neat notation for updating and accessing components of a global con guration. Although the ASM approach generally lacks the compositionality of SOS-based frameworks, the Montages presentation of ASM [6] provides a separate module for each syntactic construct, specifying how a local ASM for that construct is plugged into the global ASM, together with the ring rules associated with the local ASM. It appears that none of the above frameworks supports the de nition and combination of language modules to the same extent as action semantics does. For instance, the monadic approach to denotational semantics would require a particular composition of monad transformers to be speci ed for each module; not only would this be repetitive, but also it is unclear how to combine independently-speci ed compositions. Similarly, Modular SOS requires the speci cation of compositions of so-called label transformers—although there it would be quite straightforward to combine the transformers (since their order of composition is insigni cant, and their symbolic indices allow duplication to be avoided). The Montages approach to ASM inspired our reconsideration of the modular structure of action semantics, and it also supports combination of modules for individual constructs into a complete language. However, it seems that Montages modules from one language description cannot in general be reused without reformulation in other descriptions, since the notation used in the underlying ASM formalism may well vary. In a recent paper [9] Menezes and Moura propose a novel “component-based” style of action semantics. Although their proposal has some interesting aspects, it requires the semantic equations to be formulated in a style that is signi cantly di erent from that previously used in action semantics. We believe that our proposal in the present paper, 32 K.-G. Doh, P.D. Mosses / Science of Computer Programming 47 (2003) 3 – 36 which is based on a simple rearrangement of the pieces already found in previous action-semantic descriptions without changes to the semantic equations themselves, provides an easier route towards easier composition of programming languages by combing action-semantics modules. There is no silver bullet in designing a good programming language. However, we hope a design tool such as the one proposed in this article may be used to enhance the quality of language-design activity. Appendix A. Inclusions between sorts of semantic entities ExpressionLanguage: Datum Value Int LitValue Bool Nat FirstOrderLazyFunctionalLanguage: Datum Value Int Nat Bindable LitValue Bool Function Action Unary-Name-Function K.-G. Doh, P.D. Mosses / Science of Computer Programming 47 (2003) 3 – 36 33 FirstOrderEagerFunctionalLanguageWithEffects: Datum Storable Value Int Nat Bindable LitValue Bool Variable Function Simple-Variable Unary-Value-Function Appendix B. List of action notation symbols The following list includes only those symbols used in the examples given in this paper (a list of all symbols of AN-2 would be roughly twice as long). sorts Action Data Datum DataSort Token Bindable Bindings Cell Storable Nat Int Bool Yielder DataSort DataOp Enquirer context-free syntax Action Token Bindable Bindings Cell Storable Int Bool -> -> -> -> -> -> -> -> Datum Datum Datum Datum Datum Datum Datum Datum Datum "(" {Data ","}* ")" -> Data -> Data %% %% %% %% %% %% %% %% actions as data used in bindings used in bindings binding maps primitive storage in cells integers truth-values %% 1-tuple %% tuples 34 K.-G. Doh, P.D. Mosses / Science of Computer Programming 47 (2003) 3 – 36 Nat -> Int %% integers Action "and" Action Action "and" "then" Action Action "then" Action "tentatively" Action Action "otherwise" Action "select" "(" Action "or" Action ")" Action "hence" Action Action "before" Action "furthermore" Action -> -> -> -> -> Action Action Action Action Action %% %% %% %% %% tupling sequencing composition may fail recovery -> -> -> -> Action Action Action Action %% %% %% %% alternatives scope scope sequencing scope extension "skip" "enact" "create" "update" "inspect" -> -> -> -> -> Action Action Action Action Action %% %% %% %% %% null action performance gives storage cell changes cell contents reads cell contents "provide" Data "give" Yielder "given" Yielder "when" Enquirer Action Yielder -> -> -> -> -> Action Action Action Action Action %% %% %% %% %% %% giving fixed data giving variable data matching given data testing predicate give yielded data then perform action Data DataOp DataOp Yielder "(" {Yielder ","}* ")" -> -> -> -> Yielder Yielder Yielder Yielder "bound" "to" Yielder "closure" Yielder -> Yielder %% current binding -> Yielder %% freeze yielded action "the" DataSort "#" Nat "binding" "+" | "-" | "*" -> -> -> -> DataOp DataOp DataOp DataOp %% %% %% %% %% %% %% %% constant application composition tupling project to subsort select component construct binding integer operations action | yielder | data | datum | token | bindable | bindings | cell | storable | nat | int | bool -> DataSort %% subsort projectors Yielder "=" Yielder -> Enquirer %% equality test K.-G. Doh, P.D. Mosses / Science of Computer Programming 47 (2003) 3 – 36 %% %% %% %% %% 35 DataOp includes all data operations (and action combinators). Applications of data operations may be written infix. Prefix applications have higher priority than infix applications. Infix applications are left associative. Conventional notation for Nat, Int, and Bool is included. Acknowledgements David Schmidt provided the idea of de ning and combining language-de nition modules; Olivier Danvy encouraged us to write this paper and made numerous comments on the contents and structure of previous drafts; Tae-Hyung Choi and Soo-Kang Lee showed their expertise in Visio 10 by drawing gures in the paper; and the anonymous referees supplied useful comments and corrections. References [1] R. Cartwright, M. Felleisen, Extensible denotational language speci cations, in: M. Hagiya, J.C. Mitchell (Eds.), TACS’94, Symp. on Theoretical Aspects of Computer Software, Sendai, Japan, Lecture Notes in Computer Science, vol. 789, Springer, Berlin, 1994, pp. 244–272. [2] Y. Gurevich, Evolving algebras 1993: Lipari guide, in: E. Borger (Ed.), Speci cation and Validation Methods, Oxford University Press, Oxford, 1995. [3] J. Heering, P. Klint, Semantics of programming languages: a tool-oriented approach, ACM SIGPLAN Notices 35 (3) (2000) 39–48. [4] C.A.R. Hoare, Hints on programming language design, in: POPL’73, Proc. 1st ACM Symp. on Principles of Programming Languages, ACM Press, New York, 1973. [5] C.A.R. Hoare, C.B. Jones, Essays in Computing Science, Prentice-Hall, Englewood Cli s, NJ, 1989. [6] P. Kutter, A. Pierantonio, Montages: speci cations of realistic programming languages, J. Universal Comput. Sci. 3 (5) (1997) 416–442. [7] S.B. Lassen, P.D. Mosses, D.A. Watt, An introduction to AN-2, the proposed new version of Action Notation, in: AS 2000, no. NS-00-6 in Notes Series, BRICS, Department of Computer Science, University of Aarhus, 2000, pp. 19 –36. [8] S. Liang, P. Hudak, Modular denotational semantics for compiler construction, in: ESOP’96, Proc. 6th European Symp. on Programming, Linkoping, Lecture Notes in Computer Science, vol. 1058, Springer, Berlin, 1996, pp. 219 –234. [9] L. Menezes, H. Moura, Component-based action semantics: a new approach for programming language speci cation, in: SBLP 2001, V Brazilian Symp. on Programming Languages, 2001. [10] E. Moggi, An abstract view of programming languages, Tech. Rep. ECS-LFCS-90-113, Computer Science Department, University of Edinburgh, 1990. [11] P.D. Mosses, Action semantics, in: Cambridge Tracts in Theoretical Computer Science, vol. 26, Cambridge University Press, Cambridge, 1992. [12] P.D. Mosses, Theory and practice of action semantics, in: MFCS’96, Proc. 21st Int. Symp. on Mathematical Foundations of Computer Science, Cracow, Poland, Lecture Notes in Computer Science, vol. 1113, Springer, Berlin, 1996, pp. 37– 61. [13] P.D. Mosses, A tutorial on action semantics, tutorial notes for FME’94 (Formal Methods Europe, Barcelona, 1994) and FME’96 (Formal Methods Europe, Oxford, 1996), also available from the author at http://www.brics.dk/Projects/AS/ (March 1996). 10 Visio is a trademark of Microsoft Corporation. 36 K.-G. Doh, P.D. Mosses / Science of Computer Programming 47 (2003) 3 – 36 [14] P.D. Mosses, A modular SOS for action notation, Tech. Rep. BRICS RS-99-56, Department of Computer Science, University of Aarhus, December 1999. [15] P.D. Mosses, Foundations of Modular SOS (extended abstract), in: MFCS’99, Proc. 24th Inetrnat. Symp. on Mathematical Foundations of Computer Science, Szklarska Poreba, Poland, Lecture Notes in Computer Science, Springer, Berlin, 1999, pp. 70 –80, full version published as BRICS RS-99-54, Department of Computer Science, University of Aarhus, 1999. [16] P.D. Mosses, D.A. Watt, Pascal: action semantics, draft, Version 0.6, Available from the authors at http://www.brics.dk/Projects/AS/ (March 1993). [17] G.D. Plotkin, A structural approach to operational semantics, Lecture Notes DAIMI FN-19, Department of Computer Science, University of Aarhus, 1981. [18] D.A. Schmidt, Denotational Semantics: A Methodology for Language Development, Allyn and Bacon, Newton, MA, 1986. [19] D.A. Schmidt, The Structure of Typed Programming Languages, MIT Press, Cambridge, MA, 1994. [20] J.E. Stoy, Denotational Semantics: The Scott–Strachey Approach to Programming Language Theory, MIT Press, Cambridge, MA, 1977. [21] R.D. Tennent, Language design methods based on semantic principles, Acta Inform. 8 (1977) 97–112. [22] M.G.J. van den Brand, A. van Deursen, J. Heering, H.A. de Jong, M. de Jonge, T. Kuipers, P. Klint, L. Moonen, P.A. Olivier, J. Scheerder, J.J. Vinju, E. Visser, J. Visser, The ASF + SDF meta environment: a component-based language development environment, in: R. Wilhelm (Ed.), CC’2001, Compiler Construction, Lecture Notes in Computer Science, vol. 2027, Springer, Berlin, 2001, pp. 365–370. [23] A. van Deursen, J. Heering, P. Klint (Eds.), Language Prototyping, AMAST Series in Computing, vol. 5, World Scienti c, Singapore, 1996. [24] A. van Deursen, P.D. Mosses, ASD: the action semantic description tools, in: AMAST’96, Proc. 5th Internat. Conf. on Algebraic Methodology and Software Technology, Munich, Lecture Notes in Computer Science, vol. 1101, Springer, Berlin, 1996, pp. 579 –582. [25] K. Wansbrough, J. Hamer, A modular monadic action semantics, in: Proc. Conf. on Domain-Speci c Languages, The USENIX Association, Berkeley, CA, 1997, pp. 157–170. [26] D.A. Watt, Programming Language Syntax and Semantics, Prentice-Hall, Englewood Cli s, NJ, 1991. [27] D.A. Watt, Standard ML action semantics, draft, Version 0.5, Available from the author at http://www.brics.dk/Projects/AS/ (May 1997). [28] D.A. Watt, The static and dynamic semantics of Standard ML, in: P.D. Mosses, D.A. Watt (Eds.), AS’99, 2nd Internat. Workshop on Action Semantics BRICS NS-99-3, Department of Computer Science, University of Aarhus, 1999, pp. 155 –172.