e. strom - ibm research people and projects · e. strom da vid f. bacon arth ur p. goldb erg ......

226

Upload: lethien

Post on 24-May-2018

215 views

Category:

Documents


2 download

TRANSCRIPT

Hermes: A Tutorial and Reference ManualRobert E. StromDavid F. Bacon Arthur P. Goldberg Andy LowryDaniel YellinIBM T. J. Watson Research Center1June 27, 19901PO Box 704, 30 Saw Mill River Road, Yorktown Heights, NY 10598

iHermes Greek Mythology. The god of commerce, inven-tion, cunning, and theft, who also served as messenger and her-ald for the other gods, as patron of travelers and rogues, and asthe conductor of the dead to Hades; identi�ed with the Romangod Mercury.11([Mor80])

PrefaceThis document contains a tutorial, a reference manual, and some appen-dices.The tutorial introduces you to Hermes by guiding you through a set ofexamples. We begin with a simple program which outputs \Hello world",and continue through more complex examples, ending with a window sys-tem. We then discuss additional useful features of Hermes, including animportant innovation in static checking|typestate analysis. We assumethat you have some experience writing application programs in a high-levelprocedural language, such as C, Pascal, or Ada. We will highlight the dif-ferences between programming in Hermes and programming in the otherlanguages. This way you will get a feeling for \idiomatic Hermes". At theend of the tutorial, you will know the basic vocabulary of Hermes. You willbe able to write some Hermes programs by imitating the examples. Youwill be able to compare Hermes to other languages. But you will not knowthe precise rules of Hermes|these are covered in the reference manual.The reference manual is more formal than the tutorial. We also give ex-amples in the reference manual, but with a di�erent purpose. The examplesin the reference manual illustrate the language rules. They highlight the dif-ference between legal and illegal programs rather than illustrate \typical"programs.The appendices are the most formal. They contain the rules of Hermesin tabular form. They are produced from the same machine-readable �lesthat are used to produce the compiler itself.This document does not describe how to use any of the existing Hermesimplementations, nor does it describe their idiosyncrasies. Such informationis to be found in the Hermes Users Guide distributed with the implemen-tation.Readers wishing to obtain an experimental working Hermes system shouldsend electronic mail to [email protected] or U.S. mail to one of theauthors at the address listed on the title page. Hermes currently (March1990) runs on Sun 3 and Sun 4 systems running SunOS, and IBM RTsrunning Berkeley 4.3 Unix.

ContentsPreface iiI Tutorial 11 Introduction to Hermes 31.1 Introduction : : : : : : : : : : : : : : : : : : : : : : : : : : : 31.2 Getting Started|A Simple Hermes Program : : : : : : : : 51.3 A Second Program : : : : : : : : : : : : : : : : : : : : : : : 101.4 Putting Processes Together : : : : : : : : : : : : : : : : : : 121.5 Declarations and De�nitions : : : : : : : : : : : : : : : : : : 181.5.1 Declarations : : : : : : : : : : : : : : : : : : : : : : : 181.5.2 De�nitions : : : : : : : : : : : : : : : : : : : : : : : 211.6 A Simple Server : : : : : : : : : : : : : : : : : : : : : : : : 252 A Miniature System 312.1 Requirements : : : : : : : : : : : : : : : : : : : : : : : : : : 312.2 Design : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : 322.3 Interfaces : : : : : : : : : : : : : : : : : : : : : : : : : : : : 332.4 Window System Shell : : : : : : : : : : : : : : : : : : : : : 362.5 Front-end Process : : : : : : : : : : : : : : : : : : : : : : : 372.6 Tokenizer Procedure : : : : : : : : : : : : : : : : : : : : : : 412.7 The Window Manager : : : : : : : : : : : : : : : : : : : : : 432.7.1 De�nitions : : : : : : : : : : : : : : : : : : : : : : : 432.7.2 Skeleton : : : : : : : : : : : : : : : : : : : : : : : : : 442.7.3 Refocussing and Writing Output : : : : : : : : : : : 452.7.4 Dispatching Lines to a Particular Window : : : : : : 452.7.5 Creating and Killing Windows : : : : : : : : : : : : 472.8 Creating a Window Application : : : : : : : : : : : : : : : : 482.8.1 Application Builder : : : : : : : : : : : : : : : : : : 492.8.2 Adapter and Quit Dispatcher : : : : : : : : : : : : : 512.9 Summary : : : : : : : : : : : : : : : : : : : : : : : : : : : : 53

iv3 Type and Typestate Checking 554 Additional Hermes Constructs 594.1 Expression Blocks : : : : : : : : : : : : : : : : : : : : : : : 594.2 Send : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : 604.3 Variants : : : : : : : : : : : : : : : : : : : : : : : : : : : : : 614.4 Polymorphs : : : : : : : : : : : : : : : : : : : : : : : : : : : 645 Research Directions in Hermes 67II Hermes Reference Manual 696 Introduction 717 Lexical and Syntactic Rules 738 Resolution 758.1 Variable Names : : : : : : : : : : : : : : : : : : : : : : : : : 758.1.1 Base Variables : : : : : : : : : : : : : : : : : : : : : 768.1.2 Component Names : : : : : : : : : : : : : : : : : : : 778.2 Type Names : : : : : : : : : : : : : : : : : : : : : : : : : : : 778.3 Attribute Names : : : : : : : : : : : : : : : : : : : : : : : : 788.4 Exception Names : : : : : : : : : : : : : : : : : : : : : : : : 788.5 Exit Names : : : : : : : : : : : : : : : : : : : : : : : : : : : 798.6 Process Names : : : : : : : : : : : : : : : : : : : : : : : : : 799 Type Checking and Inference 8010 Typestate Checking 8410.1 Syntax of Typestates : : : : : : : : : : : : : : : : : : : : : : 8410.2 Formal Typestates : : : : : : : : : : : : : : : : : : : : : : : 8510.3 Valid Typestates : : : : : : : : : : : : : : : : : : : : : : : : 8610.4 Ordering of Typestates : : : : : : : : : : : : : : : : : : : : : 8710.5 Coercions : : : : : : : : : : : : : : : : : : : : : : : : : : : : 8710.6 Constants : : : : : : : : : : : : : : : : : : : : : : : : : : : : 8810.7 How to Apply the Typestate Rules : : : : : : : : : : : : : : 8810.8 The Checking Algorithm : : : : : : : : : : : : : : : : : : : : 9010.9 Typestate Errors : : : : : : : : : : : : : : : : : : : : : : : : 9111 Hermes Operations 9311.1 Ubiquitous Operations : : : : : : : : : : : : : : : : : : : : : 9311.2 The Depletion Exception : : : : : : : : : : : : : : : : : : : 9511.3 Control Flow Operations : : : : : : : : : : : : : : : : : : : : 9611.4 Scalar Operations : : : : : : : : : : : : : : : : : : : : : : : : 10111.5 Record Operations : : : : : : : : : : : : : : : : : : : : : : : 107

v11.6 Table Operations : : : : : : : : : : : : : : : : : : : : : : : : 10711.7 Variant Operations : : : : : : : : : : : : : : : : : : : : : : : 11511.8 Process Creation and Communication Operations : : : : : : 11811.9 Polymorph Operations : : : : : : : : : : : : : : : : : : : : : 12511.10Program Operations : : : : : : : : : : : : : : : : : : : : : : 12811.11Constraints : : : : : : : : : : : : : : : : : : : : : : : : : : : 131A Hermes Concrete Syntax 134A.1 Lexical Rules : : : : : : : : : : : : : : : : : : : : : : : : : : 135A.2 Syntactic Rules : : : : : : : : : : : : : : : : : : : : : : : : : 138B Hermes Operations 150B.1 Operation Descriptions : : : : : : : : : : : : : : : : : : : : : 150B.1.1 Description Header : : : : : : : : : : : : : : : : : : : 151B.1.2 Type Rules : : : : : : : : : : : : : : : : : : : : : : : 151B.1.3 Preconditions : : : : : : : : : : : : : : : : : : : : : : 152B.1.4 Postconditions : : : : : : : : : : : : : : : : : : : : : 152B.1.5 Special Rules : : : : : : : : : : : : : : : : : : : : : : 153B.1.6 Operation Semantics : : : : : : : : : : : : : : : : : : 153B.2 Type Classes : : : : : : : : : : : : : : : : : : : : : : : : : : 153B.3 Inference Functions : : : : : : : : : : : : : : : : : : : : : : : 155B.4 Precondition Functions : : : : : : : : : : : : : : : : : : : : : 155B.4.1 Typestate Preconditions : : : : : : : : : : : : : : : : 155B.4.2 Context Preconditions : : : : : : : : : : : : : : : : : 157B.4.3 Conditional Exceptions : : : : : : : : : : : : : : : : 158B.5 Postcondition Functions : : : : : : : : : : : : : : : : : : : : 158B.6 Operation Descriptions : : : : : : : : : : : : : : : : : : : : : 160C Prede�ned Module 194C.1 References : : : : : : : : : : : : : : : : : : : : : : : : : : : : 211

vi

Part ITutorial

1

1Introduction to Hermes1.1 IntroductionHermes is an experimental language developed at the IBM TJ WatsonResearch Center. It re ects our research group's opinion of how complexsystems should be programmed ([BS89, SY83, PS83, SYB87c]).The basic idea behind Hermes is to take as much work as possible awayfrom the programmer and give it to the compiler implementor. Hermes isa simple language for expressing computation and program composition.The Hermes compiler chooses the implementation instead of you. For ex-ample, if you wish to build a list of names and addresses, you just declare atable of name - address pairs. You don't worry about whether this table isstored in the computer as an array, a linked list, a hash table, a splay tree,or a disk �le|that is the compiler's job. Instead of the approximately 120system calls in a typical UNIX ([KP84]), you have just 10 Hermes state-ments which deal with creating programs, connecting them together, andcommunicating between them. You don't worry about distribution, com-munication, or coping with failures. With just these ten statements, youcan build arbitrary systems, including some which in UNIX would requiresuperuser privilege to build.Of course, a lean language isn't all that's required. After all, the Turingmachine language is even leaner! Hermes also stresses the ability to buildsystems out of separately developed, compiled, and tested modules. Weexpect non-interference between modules | this means that a moduleshould behave the same when combined with other modules into a largersystem as it did when tested in isolation. We want as much compile-timechecking as possible to detect nonsensical programs and to detect interfacemismatches. In exchange for modularity and checkability we are willing torequire programmers to write more documentation, e.g. declarations andinterface de�nitions.How does this a�ect you, the programmer? We are assuming that youare used to programming in a language like C, Pascal, or Ada, under an op-erating system like UNIX, MS-DOS, or VMS. Will you have to completelychange your concept of programming?Not really. In many respects, Hermes is a traditional imperative lan-guage. Hermes has variables, assignment statements, and the usual sequen-tial control structures. Although Hermes is explicitly designed to run inconcurrent and distributed environments, you do not need to write paral-lel execution statements (e.g. cobegin), semaphores, monitors, guardians,

4 1.1. Introductionor transactions. A Hermes module is a straightforward sequential programwith inputs, outputs, and local memory. A Hermes system is built by tak-ing several Hermes modules and connecting the output of some modules tothe input of others.But some aspects of Hermes are very di�erent from what a traditionalsystems programmer is used to:� There are no pointers. No address variables, no address arithmetic, noaddresses period. You have no control of how data is stored in mem-ory. That means that bit order, byte order, alignment, and all othersuch details which may vary from machine to machine are invisibleto you.� There is no shared data. Not via pointers, since there are no pointers,not via nesting of inner procedures within outer procedures, not viaglobal or external declarations of any kind. Every variable belongsto exactly one module. There is no aliasing|that is, two distinctvariable names always denote two distinct variables.� You will use many processes. Hermes is a process-oriented language.This means that Hermes processes take on the role that modules havein procedural languages. You wouldn't dream of building a systemwith thousands of UNIX processes, but you may well build a systemwith thousands of Hermes processes. Most of the time, communica-tion between Hermes processes is at least as fast as a procedure call.But even though there are many processes, there is no shared data|only queued communication. So you can still analyze each processindependently and view it as a sequential program.� There is no language vs. system duality. When you program in C,you have to think about the C world and the UNIX, MS-DOS orVMS world. In the C world, data is stored in typed variables suchas scalars, arrays, structs, or pointers, and passed between proce-dures using CALLs. In the operating system world, data is stored inbu�ers or in �les, and passed from one application to another usingpipes, sockets, or �les. Data is untyped|it is interpreted as stringsof bytes. Some data (e.g. pointers, �le handles) cannot be passed toother processes or stored in �les. This data must be marshalled anddemarshalled. In distributed programs, you must be aware of what islocal, what is remote, and often what kind of machine you're runningon. In Hermes, there is only one world to think about|the Hermesworld.� Hermes has much more compile-time checking ([SY86]). Much morethan in C, and somewhat more even than Ada, Modula-3, or othernewer languages. The bene�ts: (1) more errors are discovered earlier,and (2) di�erent users can safely run in the same address space, even

1. Introduction to Hermes 5though some programs may have bugs and the users don't trust oneanother. There is some cost: you have to write more declarations.1.2 Getting Started|A Simple Hermes ProgramLet's start with our �rst Hermes program.receive Parms from Init;call Parms.PutLine("Hello, World!");return Parms;Before you compile this program, you have to add declarations and pos-sibly type de�nitions. We'll show you in a later section how to write them.Let's assume for now that you have already written them or that they havealready been supplied for you.This program can be used to display the message Hello, World!. Howdoes it di�er from the corresponding program in C, Ada ([SYW85]), orPascal?printf("Hello, World!"); /* a C program to do the above */PUT("Hello, World!") -- An ADA programwrite("Hello, World!") fA Pascal program gAlthough at �rst glance the programs appear to di�er only in super�cialsyntax, there is nevertheless an important distinction between the Her-mes version of this simple program and all the others|the late binding ofPutLine. In Hermes, PutLine is a parameter; its counterparts in the otherlanguages are constants.In the C program, printf has a �xed binding|it denotes a library pro-gram which writes the character string to the standard output. The onlyway to change the binding is to link your C program to a di�erent library.If you do this, you will also change the binding of printf for all the mod-ules of your program. To use the terminology of the C reference manual([KR78]), functions are not variables. If you want to control the binding ofa function with a C program, you must simulate function variables usingpointers to functions. (Similarly in Pascal ([Coo83]) and Ada, functions arenot variables. In these languages there is a way to achieve a limited degreeof dynamic binding.)So even in this tiny Hermes program, you can see two important featuresof Hermes: (1) nothing is global, and (2) every function or procedure namerefers to a variable whose value is determined at run-time, not compile-time.Why do we do this? After all, the program is longer because we mustreceive the parameter list Parms and because we must refer to the functionas Parms.PutLine. Hermes is designed this way because it assumes you

6 1.2. Getting Started|A Simple Hermes ProgramUser address spaces

Kernel

FIGURE 1.1. A traditional operating system with many applicationsare writing a multi-user or multi-application system rather than a singleapplication.Look at �gure 1.1, which depicts a traditional system such as UNIX. Akernel maintains multiple address spaces in which you run your applica-tion programs. Your C application is a program running in a single addressspace. Resources outside the address space (e.g. terminals and printers)are managed by the operating system, and assigned to the address spaceas a whole. It makes sense for all the printf statements within an ap-plication to have a single global meaning, because \global" means withinthe address space. Joe's application and Jane's application can each havedi�erent bindings to their respective printf functions.But suppose my application is an entire system, which may include Joe'sapplication, Jane's application, modules available to both users, and mod-ules written by one user but accessible to some modules of another. (See,for example, �gure 1.2.) In this situation, it is clear that no function namecan be global to the whole system. Although Joe's application and Jane'sapplication may both be part of a single large \program", it does not make

1. Introduction to Hermes 7

FIGURE 1.2. A multi-application system viewed as a single high-level language\program"

8 1.2. Getting Started|A Simple Hermes Programsense for Joe's printf statement to denote the same console as Jane's.Hermes encourages you to think of systems from the viewpoint of �gure1.2, rather than �gure 1.1. The di�erences are: (1) the distinction betweenintra-user and inter-user communication disappears; (2) access to resourcesis controlled at the level of individual modules, rather than address spaces;and (3) access control decisions are made by programs, not by a �xedkernel.Given that Hermes avoids static binding and global variables, how is thebinding done? Program HelloWorld illustrates the simplest way of doingthe binding|making the function PutLine a parameter to the program.This is a good time to begin using the Hermes terminology. The sourcecode for program HelloWorld is called a process module. A process modulecan be instantiated to create a process|an active entity with state, whichexecutes the statements of the process module. Parms, Parms.PutLine, andInit are variable names.Every variable name has a statically known type. A variable's type deter-mines what operations on the variable are legal and what types the otheroperands must have. Types are organized into type families based on theoperations they allow. For example, Parms.PutLine is of type family out-put port. Output ports support the operation call. The value of an outputport is a connection to an input port, which is a message queue in anotherprocess.Program HelloWorld communicates with its output process by executingthe statement call Parms.PutLine. The call parameters (in this case, thesingle parameter "Hello World!") are bundled into a callmessage, whichis then queued on the input port to which the outport port is connected.The calling process waits until the callmessage is returned.The type of an output port determines the type of input port it canconnect to. The type of an input port, in turn, determines the type ofcallmessage which can be sent to it. In this example, the input port ex-pects a callmessage with a single character string parameter. The callstatement's argument list must contain a single character string. A callwith the wrong number or types of arguments will be rejected at compiletime as a type error. .When program HelloWorld starts up after being instantiated, the onlyinitialized variable is the input port named Init, which was designated asthe initialization port. By convention, the creator of a process | which wecall the parent| sends the newly created process | the child| a callmes-sage over its initialization port. The callmessage contains the parametersthe child needs to get started, including whatever ports it may need to com-municate with the outside. In this example, we assume that HelloWorld'sparent|perhaps a shell process|passes a callmessage with parametersnamed PutLine, GetLine, GetProgram, and ParmString. GetLine andGetProgram are ports to services to obtain input lines and load programs,and ParmString is a command string. These other parameters aren't used

1. Introduction to Hermes 9by the HelloWorld program, but they will be used in other examples later,and we are assuming a common interface between the shell and its children.It's easy to write a shell in Hermes. In a later section, we will see how towrite one.The receive statement dequeues the callmessage from the input portInit and stores it in the callmessage variable Parms. Now the parameters,named Parms.PutLine,Parms.GetLine, etc., are initialized. The statementcall Parms.Putline is legal here. It would have been illegal to call Parms.Putline before the receive statement, because Parms.Putline is unini-tialized at the beginning of the program. Such a call would be rejected atcompile time as a typestate error. Typestate checking ([SY86]) is a formof static checking which is new to Hermes and will be discussed in detaillater.The call statement creates a callmessage with a single string componentwith value \Hello, World!". This message is sent to the input port at theother end of the connection Parms.PutLine.Which port is that? It dependsupon the value of parameter PutLine which was passed by the parentprocess. We don't know which port it is, and we don't care. It may be aservice which writes the line on an output device. Or the port may belongto a Hermes process which �lters the output and passes it along to anotherport. When we look at the program in isolation, we care only that thecaller and receiver agree upon what is sent and returned in the call. In thiscase, the interface will say that the parameter type must be a characterstring and the parameter typestate must be initialized. Hermes guaranteesthat Parms.PutLine will be bound only to input ports expecting to receivecallmessages containing a single initialized character string parameter. .After the call completes, program HelloWorld returns the callmessagein Parms using a return statement. The callmessage is returned to theprocess which originally made the call|in this case, the parent process.After the return of the callmessage, variable Parms becomes uninitialized.It would be illegal to call Parms.PutLine here. Since there are no morestatements, the process terminates.Because Hermes is a process-oriented language, it will be less confusingif you think of making a call and returning a call as sending and returninga callmessage, rather than as transfer of control. Think of both the callerand receiver as active processes. The caller sends a callmessage and waitsfor the answer. The receiver receives the callmessage, processes it, possiblystores some return values into the callmessage, and then returns it. 1Program HelloWorld is typical of what we call a client program. A1Note that a Hermes program terminates when it reaches the end, not when itexecutes the return statement. In other languages, where a return statement canbe viewed as a transfer of control, execution of a return statement terminates theprocedure. But a Hermes process may possibly continue execution after issuingreturn. We will see some examples of this later.

10 1.2. Getting Started|A Simple Hermes Programclient corresponds to an applications program on conventional operatingsystems. Client programs have a speci�c job to do|they're not interestedin creating processes, or binding output ports. They are content to let theirparent process|typically a shell or system builder|do the binding forthem. Also, they are users of services|not providers of services. Roughlyspeaking, clients make calls, servers receive calls, and shells create andconnect other processes. Some programs may play multiple roles, e.g. botha server and a client. Clients are the most common type of process andthe easiest to write. In later sections, we'll see how to use Hermes to writeshells and servers.Summary: In this section, you have seen that in Hermes, there is noglobal data, and in particular function names (which Hermes calls outputports) are variables, not constants. You have seen that this is a conse-quence of the fact that a Hermes program is an entire dynamic system, notsimply your application. You have learned the following Hermes nomencla-ture: process module, process, variable, type, type family, typestate, inputport, output port, callmessage, initialization port, initialized, uninitialized,client, server, and shell. You have seen the structure of a typical clientprogram: �rst receive a parameter callmessage from the initialization port;then use the outport ports passed in that callmessage to access services.You have learned the following Hermes statements: receive, call, andreturn.1.3 A Second ProgramBefore moving on to the systems programming constructs in Hermes, let'slook at one more client program, which we shall call Echo:receive Parms from Init;blockdeclareLine: Charstring; -- line read from standard inputbeginwhile ('true') repeatcall Parms.GetLine(Line);call Parms.PutLine(Line);end while;on (GetLineInterface.EndStream)end block;return Parms;We assume that the initialization port Init is of the same type as inprogram HelloWorld. Therefore, the variable Parms will have the samecomponent names and types as in program HelloWorld. An input porttype, together with its associated callmessage type is called an interface,

1. Introduction to Hermes 11so we can say that program Echo and program HelloWorld have the sameinitialization interface. This interface is shown pictorially in �gure 1.3. Itis written out in full in section 1.5.GetLine and PutLine both take a single string-valued argument. How-ever, GetLine assumes that its argument is uninitialized prior to the call,and that an initialized result is passed back on return. PutLine, on theother hand, assumes that its argument is initialized prior to the call. Allthese assumptions are recorded in the interface de�nition. Both clients andservers are checked at compile-time to make sure that the code agrees withthe interface with respect to both type and typestate. Type checking ofinterfaces may be familiar from other languages, such as Ada and Pascal.Typestate checking is new. Every call interface has a pre-call and post-calltypestate. The compiler checks that a caller puts each argument in thecorrect pre-call typestate. The compiler checks that a receiver puts eachparameter in correct post-call typestate prior to return. In this example,GetLine's interface de�nition speci�es that the call argument is uninitial-ized before call and initialized after return. If the compiler can't deducethat a program receiving a call will always initialize its parameter, it willgenerate an error message and refuse to compile the program.GetLine's interface also de�nes an exception named EndStream. Whenthe server is called to get a line and there are no more lines because theend of the stream has been reached, the server will return the callmessagewith the EndStream exception. For each exception, there may be a di�erentpost-call typestate speci�ed in the interface. In this example, the interfacewill specify that the argument to GetLine will not be initialized in theevent of an EndStream exception.The structure of program Echo is an in�nite loop, calling Getline toobtain a string and store it into variable Line, and then calling PutLine toput out the string. When the EndStream exception is raised, control jumpsto the nearest matching exception handler clause in an enclosing blockstatement. A clause is simply a set of statements|in this case, the set isempty, so the block simply terminates, and the return follows.Exception handling is very important in a language like Hermes. Primi-tive statements may raise exceptions; user-de�ned operations implementedvia calls may raise user-de�ned exceptions. In some languages, a run-timeerror such as an over ow or division by zero causes the entire programto halt and an error message to be printed. In Hermes this is obviouslyunacceptable| remember the program isn't just your application, but thewhole system. Return codes such as are used in C have the disadvantagethat you might fail to test them. A Hermes exception can't be ignoredbecause control jumps to a new location.Typestate checking guarantees that you can't use a variable like Lineafter an exception return. The compiler will know that Line is uninitial-ized because GetLine's interface de�nition speci�es that its argument willbe returned uninitialized when the EndStream exception is returned. This

12 1.4. Putting Processes TogetherClient

PutLine

GetLine

GetProgramFIGURE 1.3. Interface to our typical clientdegree of checking goes beyond what even the most modern commerciallyavailable languages (as of 1989) o�er.Incidentally, the call to GetLine may also be written:Line := Parms.GetLine();Whenever a call has n arguments, and the last argument is a return value,the call may be rewritten using the familiar function notation. Like otherexpressions, functions return anonymous values which may themselves beused as operands. In this case, the result is simply assigned to the variableLine, so there is no particular advantage to the function notation.Summary: You have learned that calls are processed by exchanging acallmessage. (This is called passing parameters by value-result.) Interfacede�nitions de�ne which parameters passed as call arguments from the callerand which parameters returned to the caller are expected to be initialized.These expectations are enforced by typestate checking. You have learnedabout exceptions. You have learned the following Hermes terminology: in-terface, typestate checking, exception, exception handler clause. You haveseen examples of the following Hermes statements: assignment, functioncall, while, block.1.4 Putting Processes TogetherSo far, we have written simple one-process programs. These programs as-sumed that all ports were bound by the parent and passed as parametersin the initialization callmessage. Now you're going to learn to do your ownsystems programming. We will write a process that will instantiate twoother processes and bind the output port of one to the input port of thesecond.The interface we have been using for programs HelloWorld and Echois depicted in �gure 1.3. These programs are initially passed three ports:PutLine, GetLine, and GetProgram, so they are depicted as boxes withthree arrows representing three connections to the environment.We're going to build a composite program to the same interface. Itwill consist of our client program (either program HelloWorld or programEcho|it doesn't matter), connected to a �lter program. The �lter has one

1. Introduction to Hermes 13PutLine

Filter

PutLineFIGURE 1.4. Interface to the �lterClientGetProgram

GetLine

PutLine

Filter

PutLineFIGURE 1.5. Two interconnected processesinput port and one output port, as shown in �gure 1.4. The �lter programreceives a call as if it were a PutLine service. Each time it is called onits input port with a line as parameter, it calls its output port passingthe original line preceded by a character string called Prefix. The �lter isinitially passed its output port and the pre�x string.The composite program is a program with the same interface as in �gure1.3. It creates the original client, the �lter, and connects them togetheras shown below in �gure 1.5. Since the composite program has the sameinterface as the original single client process, it can be further composed.We make an analogy between Hermes processes and electrical compo-nents with plugs and sockets. We can think of the process in �gure 1.3as a component with 3 plugs, and the process in �gure 1.4 as a box withone plug and one socket. The combination in �gure 1.5 is a box with thesame three plugs, and can therefore be plugged into the same con�gura-tions as the �rst box. The interface corresponds to the shape of the plugs;type checking guarantees that a round plug is never plugged into a squaresocket. Unlike the pipes, �les and sockets of traditional operating systems,the data is not restricted to being byte streams in contiguous bu�ers|itcan be any datatype supported by the language.Let's look at the �lter program �rst:-- initializationreceive Parms from Init;Out := Parms.PutLine;Prefix := Parms.Prefix;

14 1.4. Putting Processes Togethernew In;connect Parms.ClientPutLine to In;return Parms;-- main loopwhile ('true') repeatreceive PutLineCM from In;call Out(Prefix | ": " | PutLineCM.Line);return PutLineCM;end while;The �lter process has two sections. The �rst section is executed just onceafter the parent instantiates the �lter and calls the initialization port. Thesecond section is executed repeatedly every time a callmessage arrives onthe �lter's In input port|this will happen when the client makes a call onits PutLine output port.Let's begin with the initialization. The �lter's interface is di�erent fromthe one we have seen in HelloWorld and Echo. Those processes' parentspassed three output ports and a string. The �lter's parent will pass oneoutput port|the connection to the PutLine services|as Parms.PutLine,and a pre�x string as Parms.Prefix. It expects to receive back an outputport, Parms.ClientPutLine, connected to the �lter's input port In.The initialization code �rst copies Parms.PutLine and Parms.Prefixinto local variables. This is necessary because the callmessage Parms willno longer be initialized after the return statement. Unlike the previousexamples, the �lter has real work to do after returning the initializationcall|it must service PutLine calls from its client.After saving the two variables it needs, it then initializes its input portIn. This is done with the new statement. It then assigns the output portvariable Parms.ClientPutLine a connection to the input port In, usinga connect statement. The connect statement is the Hermes primitive forcreating connections between output ports and input ports. It now returnsthe initialization callmessage. This callmessage will contain an output portconnected to the �lter's input port In.Recall that the return statement returns a message; it does not \transfercontrol" as it would in a language without multiple processes. The returnstatement marks the end of the initialization section of the �lter, and thebeginning of its main section.The main job of the �lter is to receive calls on input port In, and reissuethese calls with transformed data. We code this as an in�nite while loop.At each iteration of the loop, we dequeue a single callmessage into variablePutLineCM. Then we service the call by calling the `real' PutLine servicewith an argument computed by pre�xing the original string with a stringpre�x and a colon. Finally, we return the original callmessage. The �lter willrun inde�nitely until its connection is severed. We'll see in a later sectionhow this is done.Now let's look at the code of the parent process, Compound. This process

1. Introduction to Hermes 15creates the client, creates the �lter, and connects the two together. Theclient may be any program with our standard 3-plug interface, such asHelloWorld, Echo, or even Compound itself. We'll assume that the inputstring Parms.ParmString received by Compound contains the name of theclient program, and its arguments, separated by the delimiter `/'.receive Parms from Init;block beginClientName := "filter";Filter := create of Parms.GetProgram(ClientName);Mark := position of C in Parms.ParmString where(C = '/');ClientName := every of C in Parms.ParmString where(positionof C < Mark);Arguments := every of C in Parms.ParmString where(positionof C > Mark);Client := create of Parms.GetProgram(ClientName);call Filter(Parms.PutLine, "TheFilter", CliToFil);call Client(Parms.GetLine, CliToFil, Parms.GetProgram, Arguments);on (NotFound)call Parms.PutLine("compound: Syntax Error.");on (GetProgramInterface.NotFound)call Parms.PutLine("compound: " | ClientName | " not found.");on (InterfaceMismatch)call Parms.PutLine("compound: "| ClientName |" interface mismatch.");end block;return Parms;The program begins by calling the service GetProgram, passing it thecharacter string name of a program to be fetched from the program library.The result returned by GetProgram is a program. A program is a valueof the prede�ned Hermes data type program|a set of declarations andstatements of a process module stored as structured values rather thanas source text. There are three ways to obtain values of type program:(1) by passing the source text of a Hermes process through the compiler,saving the result in a program library, and retrieving it with GetProgram,(2) by writing a \program literal", and (3) by directly building a programvalue \on-the- y". In this case, we are using the �rst approach. We haveassumed that the program named "filter" has been previously compiledand that the compiled program has been placed in a library managed bythe GetProgram service.(Note: Strongly typed languages, like Algol, Ada, Pascal, C, etc., donot support on-the- y program creation. Some weakly typed 2 languages2The terms strongly-typed and weakly-typed denote respectively compile-timeand run-time checking for type violations.

16 1.4. Putting Processes Togetherlike Lisp, Scheme, and APL, allow programs as �rst-class values. Hermescombines the exibility of these languages with the static checking of theAlgol-like languages. Hermes programs, even when dynamically built, arefully checked for type and typestate errors before being instantiated asprocesses ([SYB87a, SYB87b, SY86]).To create a new process, you apply the operation create of to a valueof type program.. (Note: the syntax create of expression is used ratherthan create(expression) to distinguish primitive operations from functioncalls. Remember that function names like Parms.GetProgram are variables,whereas create is not.) The create operation instantiates a new (child)process. The code of the child process is given by the value of the operandto create. In this case, that will be the Filter program, obtained fromthe program library by GetProgram. The result of create is an output portbound to the initialization port of the �lter process.We create the client process in the same way: �rst we call GetProgramto obtain the program from the library, and then we apply the operationcreate of to this program. The only di�erence is that this time, the pro-gramname is not a constant, but must be extracted from Parms.ParmString.We will not explain this extraction in detail | one statement locates thedelimeter '/', a second obtains all characters to the left of the delimiter,the third obtains all characters to the right of the delimiter. The opera-tions position of and every of are explained in detail in section 2.6.Both processes|client and �lter|have now been created.The newly created processes cannot do anything yet. They need to beinitialized, and they depend for their initialization on receiving a call fromtheir parent. Remember that these programs (and probably most of theprograms you'll ever write) begin with receive Parms from Init. So wenow call the initialization ports of the two child processes.For Filter, we need to supply the PutLine output port, and a pre�xstring. We will receive back an output port connected to the �lter's inputport. We store this port as a variable CliToFil. In the call Client state-ment, we need to supply 3 output ports, and a string. For two of the outputports|GetLine and GetProgram, we supply the ports which we ourselvesreceived. But for PutLine, we don't want to give the client access to thesame service which we can access. Instead, we want to give the client theport CliToFil|the output port we just obtained from Filter, and whichis connected to Filter's input port. We also have to give the client a pa-rameter string, so for now, let's just send an empty string. After makingboth calls, we are �nished|the two child processes will now do all the realwork.We have written three exception handler clauses. The �rst clause han-dles a NotFound exception which can be raised by the operation positionof in the event there is no delimiter. The second clause handles an excep-tion anticipated by the GetProgram interface|namely that the requestedprogram is not found in the library. The third clause handles the builtin ex-

1. Introduction to Hermes 17ception InterfaceMismatch, which can be raised by the create statement.InterfaceMismatch is one of the few typechecking errors that cannot bedetected at compile-time, but only at run-time. For example, the secondcreate statement instantiates a child process, and returns an output portbound to the initialization port. It is known statically (that is, at compiletime) that the type of this output port must be compatible with the outputport Client. But it is not known statically whether the initialization inputport of the program is of a matching type, since GetProgram may returnan arbitrary program. If this program doesn't have the proper 3-plug in-terface (as, for example, Filter does not), then the InterfaceMismatchexception will be raised.Because modules aren't hard-wired to speci�c services, they can be reused.Any type of call can be rebound provided it is to a service of matching type.Think of the exibility of UNIX pipes extended to any kind of calls.Although this example is very simple, it illustrates the elegance of basingsystems programming on dynamic process creation and dynamic connec-tion of ports. The technique is very exible. Programs like Compound cancontain arbitrary policies for binding the ports of the programs it creates,thereby controlling their access to other processes. You don't need to bea kernel hacker or a superuser to write access control policies. You cannotaccidentally or intentionally circumvent someone else's access control. Youcannot access another user's global variables, since there are no global vari-ables. You cannot access a service by guessing its name, or by using someunde�ned value as a name. You can only get a binding to an input port Pif (1) you own P yourself and connect to it, (2) P is the initialization portof a process you create, or (3) someone else gives you a binding to P.This approach to system con�guration is called capability-based. It is anold idea in operating systems. It is not widespread, because its premise|that capabilities (access rights) are easy to store and pass around, butimpossible to forge|is often di�cult and expensive to implement. Her-mes solves this problem inexpensively by integrating the operating systemsprimitives into the language. Type-checking guarantees that you can't copyan integer, string, or other inappropriate datatype into a capability (outputport) variable. Typestate-checking guarantees that you can't use an unini-tialized and possibly garbage value of a capability. But you may createcapabilities with connect, and copy them or pass them around in mes-sages as much as you please.Summary:We have studied the program Filter|our �rst example ofa program which was both a client and a server. You have learned thata server is typically divided into an initialization part which imports andexports ports, and a service loop which handles requests from its clients.You have learned how to implement the basic functions of a shell|creatingprocesses, and setting up port connections between processes. You havelearned that Hermes is a capability-based operating system encapsulatedwithin a programming language. You have learned the following Hermes

18 1.4. Putting Processes Togetherconstructs: the new input-port, connect and create statements, and theInterfaceMismatch exception. Congratulations! You now know six of theten Hermes system programming statements!1.5 Declarations and De�nitionsLet's take a break from learning new kinds of Hermes statements, and tie upa very important loose end mentioned earlier|declarations and de�nitions.There is much debate and di�erence of opinion about the role of typechecking. Should type checking be done at compile-time or run-time? Howmuch should the programmer have to write out as explicit declarations,and how much should be inferred by the compiler? Should types be poly-morphic?Hermes takes the following positions:� All programs will produce semantically well-de�ned localized e�ects|either a normal result or an exception. This means programming bugscan't crash the system, or write random garbage into another process.In fact, a process can access only the data belonging to it.� Programming errors will be detected at compile time wherever pos-sible.� Programmers must declare the types of all variables except expressiontemporaries. The compiler infers the types of expression temporaries.� There are no polymorphic types. They're not needed as much whenyou have the ability to create program objects on the y. Instead ofwriting a polymorphic sort program which can sort any type, oneinstead writes a template and then creates speci�c sort programs bysubstituting speci�c types into the template. This accomplishes thesame goal|namely, reusable source code packages|without explic-itly introducing polymorphism. (Hermes has a polymorph type, whichallows a variable to hold a value of any type, thereby deferring typechecking until run-time.)1.5.1 DeclarationsWe will now show you the complete text of the programs whose executablestatements we showed you earlier.HelloWorld: using(Standard) process(Init: StandardIn)declareParms: StandardInterface; -- callmessage received from parentbeginreceive Parms from Init;

1. Introduction to Hermes 19call Parms.PutLine("Hello, World!");return Parms;end processThe statements Init: StandardIn and Parms: StandardInterfacearecalled declarations. A declaration consists of an identi�er, a colon, and atype name. In HelloWorld, we declare two identi�ers|Init (the initializa-tion port), and Parms (the callmessage). A type name is either a prede�nedtype name (such as integer, charstring, program, etc.), or a user-de�nedtype name.Some types, like Charstring are prede�ned. Others, like StandardInare user-de�ned. You may group collections of de�nitions into �les calledde�nitions modules. You must import the de�nitions modules containingthe de�nitions of the types you name in your source program. You mustcompile a de�nition module and place it into a library before you can importthe module.You import a de�nitions module by including its name on an imports listintroduced by the word using. The types de�ned in the imported modules,and the prede�ned types are said to be visible within the source program.No other types are visible|that is, the compiler will not understand anyother type names. If a type name is de�ned more than once (e.g. in twodi�erent imported modules, or as a prede�ned type and in an importedmodule), that name must be disambiguated by pre�xing it with the nameof the de�nitions module or with predefined.In the example, the de�nitions module named Standard is imported. Itwill contain de�nitions for StandardIn and StandardInterface. The typename StandardInterface could optionally have been written as Standard! StandardInterface, and this would have been necessary if StandardInterfacehad also been the name of a prede�ned type or if another imported de�ni-tions module contained a type named StandardInterface.The declaration of the initialization port appears in a special place|inparentheses after the word process. That's because the initialization porttype is part of the interface to the process | it needs to be known byany process wishing to instantiate this process. The other declarations areinternal, and appear between the keywords declare and begin.Here is the entire program Echo:Echo: using(Standard) process(Init: StandardIn)declareParms: StandardInterface; -- callmessage received from parentbeginreceive Parms from Init;blockdeclareLine: Charstring; -- line read from standard inputbegin

20 1.5. Declarations and De�nitionswhile ('true') repeatcall Parms.GetLine(Line);call Parms.PutLine(Line);end while;on (GetLineInterface.EndStream)end block;return Parms;end processNotice that except for the program name, the �rst �ve lines and the lasttwo are identical to HelloWorld. It would be easy for an editor or othertool to insert these lines automatically as boilerplate around any standardclient program. If this is done, then HelloWorld once again becomes aone-line program, and Hermes doesn't appear so verbose.In this example, there is an inner block statement containing a declara-tion. If a variable name is declared within a block, then that name is visibleonly within the block. This rule is found in every block structured language.Hermes doesn't allow you to hide an outer declaration by declaring a newvariable with the same name in an inner block. This avoids having \holes"in the region of visibility of a variable. This rule does not exist in mostother block structured languages. You may have to watch out for namecon icts if you copy code containing blocks from one program to another.Here are the programs Filter and Compound. This time, we'll just showthe header and declarations without repeating the code:Filter: using(Standard, Filter) process(Init: FilterIn)declareParms: FilterInterface; -- initialization callmessageIn: PutLineIn; -- input port: requests from clientOut: PutLineOut; -- output port: bound to putline servicePutLineCM: PutLineInterface; -- callmessage containing thelinePrefix: Charstring; -- string to prepend to each output linebegin-- code for process Filterend processCompound: using(Standard, Filter) process(Init: StandardIn)declareParms: StandardInterface; -- initialization callmessageFilter: FilterOut; -- port to initialize filterClient: StandardOut; -- port to initialize clientCliToFil: PutLineOut; -- output port connected to filterClientName: charstring; -- name of client programArguments: charstring; -- arguments to client programMark: integer; -- position of "/" delimiting name/parametersbegin

1. Introduction to Hermes 21-- code for process compoundend processYou only need to declare the type of simple identi�ers used as variablenames. These identi�ers are called base variables. Structured variables (e.g.records and callmessages) contain parts called components which are namedusing the dot notation common to many procedural languages. The typeof a component like Parms.GetLine is inferred from the type of the basevariable Parms.When you write an expression, you are implicitly declaring a tempo-rary variable to hold the result of that expression. Examples of expressionsare function calls (e.g. Parms.GetLine()), arithmetic operations (e.g. A+B)and literals (e.g. 'true'). The compiler infers the type of an expressionusing inference rules. In certain cases, the type of an expression cannot beinferred, and you must precede the expression with a type speci�er. Youmay write a type speci�er even when it is not necessary|for example, youmay write boolean # 'true' instead of 'true'. In that case, the type youwrite down is checked against the type that is inferred.1.5.2 DefinitionsNow let's look at the de�nitions modules. Let's begin with Standard|thede�nitions describing what a standard client is passed on initialization, andthe interfaces to the standard services GetLine, PutLine, and GetProgram:Standard: using() definitionsPutLineInterface: callmessage(Line: Charstring)constant(Line)exit ffullg;PutLineIn: inport of PutLineInterface ffullg;PutLineOut: outport of PutLineIn;GetLineInterface: callmessage(Line: Charstring)exit finit(Line)gexception EndStream fg;GetLineIn: inport of GetLineInterface fg;GetLineOut: outport of GetLineIn;StandardInterface: callmessage(GetLine: GetLineOut,PutLine: PutLineOut,GetProgram: GetProgramOut,ParmString: Charstring)constant(GetLine, PutLine, GetProgram, ParmString)

22 1.5. Declarations and De�nitionsexit ffullg;StandardIn: inport of StandardInterface ffullg;StandardOut:outport of StandardIn;GetProgramInterface: callmessage(Name: Charstring,TheProgram: Program)constant(Name)exit finit(Name), full(TheProgram)gexception NotFound finit(Name)g;GetProgramIn: inport of GetProgramInterface finit(Name)g;GetProgramOut: outport of GetProgramIn;end definitionsModule Standard does not import any other de�nitions modules. It mayonly refer to types de�ned within Standard itself, or to prede�ned types.This module de�nes four call interfaces. In our previous analogy of pro-cesses as electrical components with plugs and sockets, interfaces de�nitionsdescribe the shapes of the plugs and sockets. A call interface de�nes theinformation that can be communicated between two processes.Most high-level languages provide a mechanism for de�ning the numberand type of a procedure's parameters. Pascal also lets a programmer dis-tinguish constant and var parameters; Ada lets you distinguish in, out, andinout parameters ([Ada83]). Hermes allows (and requires) you to specifyeven more information on a interface.You de�ne an interface separately from the processes that use it; inter-faces must be de�ned in definitions modules and processes in processmodules. This separation makes an interface available to any process thatwants to use it. The compiler requires that a process module which usesan interface conform to the interface. The interface de�nes what a callerpromises to send (and therefore what the receiver expects to receive); sim-ilarly it de�nes what a receiver promises to return (and therefore whatthe caller expects to receive upon return). Any caller and receiver whichconform to the same interface can communicate.Each interface typically includes three Hermes type de�nitions: a callmes-sage, an input port, and an output port.A callmessage de�nition contains a declaration of the name and type ofeach parameter. In the calling process, you must supply a list of argumentsof matching type. In the called process, you name the parameters usingselected component notation, e.g. Parms.PutLine.You may include a constants list in the callmessage de�nition. This isa list of those parameters whose values must not change during the call.You must supply a list of the exceptions which may be returned by thecalled process.You now need to supply the additional information which is requiredfor typestate checking. A typestate is a set of program attributes known

1. Introduction to Hermes 23statically to hold at a particular program point. Like types, typestates areknown statically. Like states, they vary from statement to statement. Cer-tain typestate attributes are always tracked|such as whether a variable isinitialized or uninitialized. Other attributes are only tracked when speci�-cally de�ned by the programmer.For now, let's just look at initialization typestates. When the attributeinit(X) appears in a typestate this means that the variable X is initialized.When init(X) is absent, it is uninitialized. A Hermes variable is alwaysstatically known to be initialized or uninitialized; it is never conditionallyinitialized.3You must supply the exit typestate|the typestate the parameters musthave on normal (non-exception) return from the call. For example, the nor-mal exit from the GetLine is init(Line), which means that an initializedLine will be returned. For each exception, you must supply the types-tate the parameters must have if the call completes with that exception.The normal and exception typestates may be di�erent. For example, if ex-ception EndStream is returned, then Line will not be initialized, becauseinit(Line) is not present.An input port de�nition speci�es the type of callmessage which will besent to that input port, and the typestate that callmessage will have whenit is sent to the input port (the entry typestate). For example, GetProgramInde�nes an input port type which will hold callmessages of type GetProgramInterface.These callmessages have two components or parameters, named Name andTheProgram. A callmessage arriving at a port of type GetProgramIn is ex-pected to have component Name initialized, but not component TheProgram,because the entry typestate is init(Name).The abbreviation full is used to avoid writing out fully initialized type-states at length. For example, type program is a record with componentsDefinitions Modules, Main Program, and Programs.We write full(TheProgram)in the interface instead of the much longerinit( TheProgram ), init( TheProgram.Definitions Modules ),init( TheProgram.Main Program ), init( TheProgram.Programs )Where full appears all by itself, it means that the entire callmessageis fully initialized. So the exit typestate of StandardInterface could havebeen written asinit(GetLine), init(PutLine), init(GetProgram), init(ParmString)There are other typestate attributes besides init. For example, the at-tribute checked applies to a program value and it means that the programis not only fully initialized, but also that it is free of compile-time errors.Unchecked programs must be checked before they may be instantiated with3You can use a variant type, described in section 4.3, to get the e�ect ofconditional initialization.

24 1.5. Declarations and De�nitionsthe create of statement. The de�nition GetProgramInterface speci�esthat programs returned by GetProgram have already been checked.An output port de�nition simply speci�es the type of the input port towhich the output port connects|called the matching input port type. Thecallmessage type and typestate required for sending on the output port aredetermined from that input port type.The interface de�nitions make typestate checking possible even thoughcalling and called programs are separately compiled. When compiling thecaller, the compiler checks that the entry typestate holds before the call,and computes the exit and exception typestates using the interface. Whencompiling the called program, the compiler uses the interface to deducethe entry typestate, and checks that the correct exit typestate holds at thepoint of a return statement.See if you can work out for yourself what the de�nitions module Filtermust look like. Then check yourself against the de�nition given:Filter: using(Standard) definitionsFilterInterface: callmessage(PutLine: PutLineOut,Prefix: Charstring,ClientPutLine: PutLineOut)constant(PutLine, Prefix)exit ffullg;FilterIn: inport of FilterInterface finit(PutLine), init(Prefix)g;FilterOut: outport of FilterIn;end definitionsNotice that Filter has to import Standard, since it uses a type (PutLineOut)de�ned in Standard. Standard refers only to prede�ned types and typesde�ned within itself.Note: Hermes uses name equivalence for type de�nitions. This means, forexample, that if you de�ne two di�erent callmessage types which happen tohave the same structure|same component types, same exceptions, etc.|they are treated as two di�erent types. If you intend two variables to beused in the same contexts, be sure to declare them as the same type.Summary: In Hermes, all base variables must be declared. All user-de�ned types must be de�ned in separate modules. Only the names ofprede�ned types and of types de�ned in imported de�nitions modules arevisible. You have learned how to write process headers, declarations, andde�nitions modules, and the syntax for type de�nitions of input ports, out-put ports, and callmessages. You know when you need to put a type spec-i�er on an expression. You have learned the following terminology: typespeci�er, de�nitions module, import, visible, prede�ned, entry typestate,exit typestate, checked attribute, name equivalence. You now understandthe entire skeleton of the Hermes language and can write and execute Her-

1. Introduction to Hermes 25Quit

PutLineBuffer

GetLineFIGURE 1.6. A bounded bu�er processmes programs. The only thing you do not know now are some additionalHermes datatypes and Hermes statements.1.6 A Simple ServerWe have seen one example of a server, namely the Filter. This wasn't atypical server, since it made only a single operation available, and didn't re-ally keep any local state, but instead passed all requests through to anotherservice.Let's look at a more typical server|one more like the objects of object-oriented languages. Our server will implement a bu�er. A bu�er supportsthe operations GetLine and PutLine. Calls to PutLine insert data into a�rst-in, �rst-out queue; calls to GetLine remove the data from the queue.If the queue is empty, calls to GetLine will block until the queue becomesnon-empty. For a bounded bu�er, calls to PutLine will block if the numberof lines in the queue exceeds some capacity. We will supply one additionaloperation, Quit which is simply a command to terminate. Figure 1.6 showsthe interface.Naturally, we implement a bounded bu�er in Hermes with a process.Here is the source module:BoundedBuffer: using(Standard, BBExternal, BBLocal, Quit)process(Init: BBIn)declareParms: BBInterface; -- initialization callmessagePut: PutLineIn; -- PutLine serviceGet: GetLineIn; -- GetLine serviceQuit: QuitIn; -- Quit serviceCapacity: integer; -- maximum number of messagesQueue: Lines; -- ordered set of lines

26 1.6. A Simple ServerRunning: boolean; -- true if process has not been shut downGetCM: GetLineInterface;PutCM: PutLineInterface;QuitCM: QuitInterface;begin-- initializationreceive Parms from Init;new Put; connect Parms.Put to Put;new Get; connect Parms.Get to Get;new Quit; connect Parms.Quit to Quit;Capacity := Parms.Capacity;new Queue;return Parms;-- service loopRunning := 'true';while (Running) repeatselectevent Put and where(size of Queue < Capacity)receive PutCM from Put;insert copy of PutCM.Line into Queue;return PutCM;event Get and where(size of Queue > 0)receive GetCM from Get;remove GetCM.Line from Queue[0];return GetCM;event Quitreceive QuitCM from Quit;Running := 'false';return QuitCM;otherwise -- should not occurend select;end while;end processThe BoundedBuffer process supports three operations. It supplies aninput port for each operation. The parent will call the initialization portand pass a parameter Capacity. On return, it will receive connections to thethree input ports. The initialization code saves the Capacity parameter,creates the three input ports, makes connections to these input ports, andreturns.The service loop consists of a construct called a guarded select statement.The statement contains clauses called alternatives. Each alternative hasan optional event guard, naming an input port, and an optional booleanguard. When the select statement is executed, all the boolean guardsare evaluated. Alternatives with a false boolean guard are disabled; thealternatives with a true boolean guard or no boolean guard are enabled.

1. Introduction to Hermes 27If all alternatives are disabled, then the otherwise clause is executed. Ifone or more alternatives are enabled, one of them is chosen, and controltransfers to that alternative. However, an alternative with an event guardcan only be chosen if that input port has at least one waiting callmessage.If all the enabled alternatives have event guards referring to empty inputports, the select statement blocks until a message arrives at one of theseports.In this example, the Quit alternative is always enabled; the Put alter-native is enabled provided the number of lines in the queue is less thanCapacity, and the Get alternative is enabled provided there are any linesin the queue. The otherwise clause, although syntactically mandatory,can never be reached. It is easy to modify this program into an unboundedbu�er. Just leave out the queue size test from the Put service.The bounded bu�er process uses four interfaces: (1) BBLocal (not shown),which contains de�nitions for types used internally by BoundedBuffer (inthis case, the type Lines used to implement the queue), (2) BBExternal,which de�nes the initialization interface for the bounded bu�er, (3) Standard,which de�nes the PutLine and GetLine, and (4) Quit, which de�nes theinterface for the Quit operation. Standard and Quit are in separate mod-ules, because they will be reused in applications other than the boundedbu�er.The interface de�nition, BBExternal, is straightforward, and uses onlylanguage features we've already described. Try to write it yourself and thencheck with the de�nition here:BBExternal: using(Quit, Standard) definitionsBBInterface: callmessage(Capacity: integer, -- maximum number of messages allowedPut: PutLineOut, -- port for writersGet: GetLineOut, -- port for readersQuit: QuitOut) -- port for shutdownconstant(Capacity)exit ffullg;BBIn: inport of BBInterface finit(Capacity)g;BBOut: outport of BBIn;end definitionsThe Quit de�nition is even simpler:Quit: using() definitionsQuitInterface: callmessage() exit fg;QuitIn: inport of QuitInterface fg;QuitOut: outport of QuitIn;end definitionsHow does Hermes di�er from data abstraction languages like Ada orobject-oriented languages like Smalltalk? In both cases, we have the ability

28 1.6. A Simple Serverto write data abstractions which export a speci�c set of operations whilenot exposing the implementation details to the caller. There are some dif-ferences:� Hermes processes are active. If there are multiple callers, the callsare queued and processed serially. The process can choose not toreceive certain calls at certain times. There is no need for monitorsor semaphores or other mutual exclusion mechanisms.� I can write di�erent Hermes processes with the same interface anddi�erent internals. In Ada, for instance, I can't have two variablesof the same private type with the same speci�cation (interface) anddi�erent private parts.� I control access in Hermes on a port-by-port basis. In Ada or inobject-oriented languages, I control access on an object basis. Havingaccess to an object automatically gives access to all its operations. Inthe case of a bounded bu�er, this would mean that anyone who couldcall GetLine could also call PutLine and Quit. This is undesirable,and in fact I don't want the readers and writers of the bounded bu�erto even have to know that the Quit operation exists.The Queue is an example of a type family we haven't studied before|an ordered table. A table is simply a collection of values of the same type(mathematically, a bag). An ordered table is a table whose elements aretotally ordered|that is, each element has an associated position. Positionsare denoted by integers. The �rst position is 0. If there are k elements inthe table, the last position is k � 1.You have already used ordered tables without knowing it, since the pre-de�ned type Charstring is an ordered table of elements of type Char.In Hermes you can de�ne tables of any type|tables of integers, tables ofcallmessages, etc. You can even de�ne a recursive table type|a type Twhich is a table whose elements are of type T. In our example, we wantto de�ne a type Lines as an ordered table whose elements are of typeCharstring. The type de�nition looks like this and will appear in the def-initions module BBLocal:Lines: ordered table of Charstring finitg;The de�nition says that Lines is an ordered table, that each element isa Charstring, and that elements inserted into, removed from, or copiedfrom the table have typestate init. Notice that like other type de�nitionsin Hermes, and unlike type de�nitions in many other languages, table typede�nitions say nothing about the representation (e.g. array, linked-list, disk�le), and nothing about how much storage to allocate.All tables support the following operations: inspect an element in thetable with a particular content, insert elements into the table, remove ele-ments from the table, merge one table into another, extract or copy out a

1. Introduction to Hermes 29subtable from a table, �nd the size of the table, and iterate (repeat a setof statements once for each element of the table).Ordered tables additionally support these operations: insert, remove, orinspect an element or merge a table at a particular position, locate theposition of an element with particular contents, and iterate in order.In BoundedBuffer, the single action of the Put service consists of in-serting the designated line into the end of the table Queue. The insertstatement inserts an element into the end of a table by default if the tableis ordered. However, you may insert elements at any position in the tableif you wish, by writing:insert Element into Table at (Position);In this case, you must supply an integer-valued position. For example, ifyou specify position 3, the new element appears at position 3. If there wasan element previously at position 3, it moves to position 4, and similarlyall elements at higher positions have their positions incremented. If you tryto insert something at position 3, there must be an element at position 2,or else you will get a RangeError exception.One more technical point about insert: the statementmoves its operandinto the table, rather than copying it. This means that after the statementis executed, the source operand is discarded. Since we're not allowed todiscard PutCM.Line|in the de�nition of PutLineInterface, parameterLine is constant|we must move a copy of PutCM.Line. That explains whywe wrote the expression copy of.The Get service clause removes the �rst entry from the table and places itin GetCM.Line. When you remove the �rst element from an ordered table,the positions of all the other elements move up by one, so the previoussecond element now becomes �rst.The Quit service call terminates the loop, and hence terminates theprocess.Let's look more closely at process termination.A process terminates afterexecuting its last statement. All variables which remain initialized are �-nalized|that is, their values are discarded. In BoundedBuffer, that wouldbe the queue, the boolean variable Running, the three service ports, andthe initialization port. For most Hermes data types, discarding the valuemeans simply throwing it away. Callmessages and ports are treated spe-cially: When a callmessage, or any value which can contain callmessages(e.g. an input port which may have several callmessages on its queue) isdiscarded, the callmessages are returned. Each callmessage type has a de-fault Discarded exception, e.g. GetLineInterface.Discarded. When aninput port is discarded, connections to that port are broken; attempts tosend messages over those connections will fail and the caller will receive aDisconnected exception. Any process which becomes isolated because allits connections to the outside world are broken is equivalent to a terminatedprocess and can be safely garbage-collected by the implementation.

30 1.6. A Simple ServerThis has the following consequences: To give a process temporary accessto a service, while retaining the ability to revoke access, give out a con-nection to a �lter process which also supports a Quit service that you callwhen you want to revoke access. If you're programming a shell which isgoing to create processes running arbitrary programs which you're afraidmight run wild, make sure that on initialization, you only pass revocableoutput ports. Then if you revoke all the ports, the process will becomeisolated, e�ectively cancelling the process.Summary: We have shown the structure of a typical server: an in-put port per service, some local state, and an iterated select statement.Boolean guards may be used to prioritize the calls or to conditionally dis-able certain services. We have claimed that server processes achieve theinformation hiding of data abstraction and object-oriented languages butwith additional advantages. You have learned about the table and orderedtable type families. You have learned what happens when processes ter-minate, and that you can use the ability to terminate �lters as a meansfor access revocation and termination of child processes. You have learnedthe following Hermes concepts: alternative clauses, enable and disable,boolean guard, event guard, Discarded exception, Disconnected excep-tion, RangeError exception, process termination, the select, insert andremove statements, the copy of and size of operations.

2A Miniature SystemOur �nal example will be a dynamic system|a simpli�ed window system.Since the real purpose of this section is to illustrate systems programmingwith Hermes and not to design the ideal window system, we will eliminatethe hairy details of graphics, tiling, etc., and strip the problem to the barebones.Most of the Hermes constructs used in this example are already familiar;however we will introduce and explain a few new constructs as we go along.2.1 RequirementsSuppose I have a terminal device. Let's assume that it uses the same over-simpli�ed terminal interface we have been using in the previous examples:GetLine and PutLine. I now would like to multiplex this terminal so thatI can run several client applications. Each client application was originallywritten to use the terminal device, so they use the GetLine and PutLineinterfaces, too.I want to divide my terminal into logical windows. Each application runsin its own window. Output from an application will be directed to itswindow. Input from the terminal will be directed to the window currentlyin focus. From the terminal, I can also (1) change the current focus, (2)start up a new application in a new window, or (3) kill a window togetherwith the application running in it.In this example, we will implement these functions, using our limited,line-at-a-time interface, as follows: Every window will have a characterstring name. When a line is written out by an application, it will appearon the terminal in the form <WindowName>:<Line>. To remind the userwhich window is in focus, the name of window currently in focus will betyped out as a prompt. Every input line not headed by an escape character(!) is assumed to be typed into the current window. Every input line headedby an escape character is treated specially by the window manager. Thereare �ve escape sequences:� !!line - dispatch !line to the window currently in focus. This ishow you send data which begins with an escape character.� !C windowname application parms - create a window windownamerunning application� !F windowname - change focus to windowname

32 2.1. RequirementsQuitInputToWindow InputToWindow

quitdispatcherquitdispatcher

windowmanager

frontend

PutLine

WriteToWindow

Refocus

PutLine GetProgramGetLine

Dispatch

Adapter ClientBuffer

Window Application Window Application

Buffer Client Adapter

Kill Create

FIGURE 2.1. Structure of the window system� !K windowname - kill windowname� !Q - quit window systemThis is obviously not a realistic window manager, but its structure canbe used as a guide to building something more practical. It wouldn't behard to modify the code to use a graphical screen, and to use mouse clicksrather than escape sequences to change the focus and to create and destroywindows. The basic structure of the system remains the same.2.2 DesignThe structure of the system is shown in �gure 2.1.

2. A Miniature System 33Quit

quitdispatcher

Buffer Client Adapter

GetProgramWriteToWindowInputToWindow

QuitQuit

GetProgram

GetLine PutLineFIGURE 2.2. Structure of a Window System ApplicationA front-end process reads lines from the terminal, parses them, and in-vokes one of the services: Refocus, Kill, Create, or Dispatch. The front-end also keeps track of which window (if any) is currently in focus.The window manager process supports these four services called by thefront end. The window manager process services WriteToWindow calls fromapplications sending lines to their window. For each window, the windowmanager has a pair of ports: (1) InputToWindow, for directing data to theapplication running in that window, and (2) Quit to shut down the window.Each application consists of three processes: (1) the client itself, withthe same standard interface familiar from HelloWorld, (2) an unboundedbu�er process, similar to the BoundedBuffer described above which storeslines typed ahead to the client, and (3) an adapter process, Adapter. Theseprocesses are shown in detail in �gure 2.2, The adapter is similar to Filter.It converts PutLine calls into the appropriate WriteToWindow calls, andpasses GetProgram calls. Both the unbounded bu�er and the adapter areprepared to receive Quit calls when the window manager decides to killthe window. When a Quit call is received, the unbounded bu�er and theadapter shut down, isolating the client and thus terminating it.2.3 InterfacesOnce we have designed the module structure, we can write the de�nitionsmodules. Here are the interface de�nitions for the window manager:WMExternal: using(Standard) definitions

34 2.3. Interfaces-- initialization interface for window managerWMInterface: callmessage (GetProgram: GetProgramOut, -- service for loading programsPutLine: PutLineOut, -- write a line to the physical terminalDispatch: DispatchOut, -- dispatch a line to the appropriatewindowRefocus: WindowOut, -- change the current focusKill: WindowOut, -- kill the specified windowCreate: CreateOut) -- make a new windowconstant(GetProgram, PutLine)exit ffullg;WMIn: inport of WMInterface finit(GetProgram), init(PutLine)g;WMOut: outport of WMIn;-- interface to operation DisptachDispatchInterface: callmessage (WindowName: Charstring, --- window to which text is being sentLine: Charstring) -- text to dispatchconstant(Line)exit ffullgexception NotFound ffullg; -- no such windowDispatchIn: inport of DispatchInterface ffullg;DispatchOut: outport of DispatchIn;-- interface to operations Refocus, KillWindowInterface: callmessage (WindowName: Charstring) -- name of window operandconstant(WindowName)exit ffullgexception NotFound ffullg;WindowIn: inport of WindowInterface ffullg;WindowOut: outport of WindowIn;-- interface to operation CreateCreateInterface: callmessage (WindowName: Charstring, -- name of window being createdProgramName: Charstring, -- name of client program to runParmString: Charstring) -- parameters to client programconstant(WindowName, ProgramName, ParmString)exit ffullgexception Duplicate ffullgexception CreateFailure ffullg;CreateIn: inport of CreateInterface ffullg;CreateOut: outport of CreateIn;-- interface to operation WriteToWindowWriteToWindowInterface: callmessage (

2. A Miniature System 35WindowName: Charstring, -- name of window being output toLine: Charstring) -- output stringconstant(WindowName, Line)exit ffullg;WriteToWindowIn: inport of WriteToWindowInterface ffullg;WriteToWindowOut: outport of WriteToWindowIn;end definitionsEach window application needs to be initialized with a program name(needed by the application builder) and parameters (needed by the client),access to the WriteToWindow and GetProgram services, and the name ofthe window (needed by the �lter). The application must export connectionsto the bu�er process and to the quit service. So here are the de�nitions forthe initialization of an application:StartWindowApplication: using(Standard, BBExternal, WMExternal,Quit)definitionsWindowApplicationInterface: callmessage(ProgramName: Charstring, -- name of client program to runParmString: Charstring, -- parameters to client programGetProgram: GetProgramOut, -- service for loading programsWriteToWindow: WriteToWindowOut, -- for writing lines to awindowWindowName: Charstring, -- name of windowInputToWindow: PutLineOut, -- for writing into input bufferQuit: QuitOut) -- for killing the applicationconstant(ProgramName, ParmString, GetProgram, WriteToWindow,WindowName)exit ffullgexception NotCreatedfinit(ProgramName), init(ParmString),init(GetProgram), init(WriteToWindow), init(WindowName)g;WindowApplicationIn: inport of WindowApplicationInterfacefinit(ProgramName), init(ParmString),init(GetProgram), init(WriteToWindow), init(WindowName)g;WindowApplicationOut: outport of WindowApplicationIn;end definitionsThe three processes comprising an application are: the client, the boundedbu�er, and the adapter. The client will have interface Standard. This fol-lows from the original problem statement which speci�ed that any pro-gram which had been written to communicate with the terminal can runwithin a single window. The bounded bu�er has interface BBExternal, andthe adapter has interface Adapter. We have already designed BBExternal.Since the adapter is like the �lter, we will design its interface by making

36 2.3. Interfacesa small change to the interface Filter. Recall that the adapter exports aport to its Quit service as well as a ClientPutLine port:Adapter: using(Standard, WMExternal, Quit) definitionsAdapterInterface: callmessage(AdapterToWindow: WriteToWindowOut, -- port to window managerWindowName: Charstring, -- window name for directing outputGetProgram: GetProgramOut, -- port to GetProgramServiceQuit: QuitOut, -- port to shutdown adapterClientPutLine: PutLineOut, -- port from client to adapterClientGetProgram: GetProgramOut) -- port from client to adapterconstant(AdapterToWindow, WindowName, GetProgram)exit ffullg;AdapterIn: inport of AdapterInterfacefinit(AdapterToWindow), init(WindowName), init(GetProgram)g;AdapterOut: outport of AdapterIn;end definitionsFinally, the front end process must be given the GetLine, PutLine, andGetProgram ports, and the ports to the four front-end accessible servicesof the window manager. Its interface is:FEPExternal: using(Standard, WMExternal) definitionsFEPInterface: callmessage(GetLine: GetLineOut,PutLine: PutLineOut,GetProgram: GetProgramOut,Dispatch: DispatchOut, -- dispatch a line to the appropriatewindowRefocus: WindowOut,Kill: WindowOut,Create: CreateOut)constant(GetLine, PutLine, GetProgram, Dispatch, Refocus, Kill,Create)exit ffullg;FEPIn: inport of FEPInterface ffullg;FEPOut:outport of FEPIn;end definitions2.4 Window System ShellNow that the interfaces are all written, the programs are fairly easy to write.Let's begin with the outer shell of the system. It simply creates the front endprocess and the window manager. The outer shell is similar to the program

2. A Miniature System 37Compound which we wrote earlier. It creates the window manager process.It calls the initialization port of the window manager, giving it access toGetProgram and PutLine, whereupon it receives back ports to four of the�ve window manager services. (The �fth service, WriteToWindow, is onlyavailable to created applications and none have been created yet.) It theninitializes the front end process, passing GetLine, GetProgram, and thefour services:WMSystem: using(Standard, WMExternal, FEPExternal)process(Init: StandardIn)declareParms: StandardInterface; -- initialization callmessageDispatch: DispatchOut; -- dispatch service in WMRefocus: WindowOut; -- refocus service in WMKill: WindowOut; -- kill service in WMCreate: CreateOut; -- create service in WMbeginreceive Parms from Init;call ( WMOut # (create of Parms.GetProgram("windowmanager")))(Parms.GetProgram, Parms.PutLine, Dispatch, Refocus, Kill,Create);call ( FEPOut # (create of Parms.GetProgram("frontend")))(Parms.GetLine, Parms.PutLine, Parms.GetProgram, Dispatch,Refocus,Kill, Create);return Parms;end process2.5 Front-end ProcessThe front-end process is a simple loop in which a line is read, and a decisiontree is followed to one of the decisions Dispatch, Refocus, Kill, Create,or error. Here is the program. We leave it to you to �ll in the declarations.running := 'true';receive Parms from Init;tokenizer := procedure of Parms.GetProgram("tokenizer");WindowName := ""; -- no window in focusblock beginwhile (running) repeatblock declareLine: Charstring; -- line read by frontendEscape: Char; -- escape character removed from linebegincall Parms.PutLine( WindowName | ">"); -- prompt user

38 2.5. Front-end Processcall Parms.GetLine(Line);if size of Line = 0thencall Parms.Dispatch(WindowName, Line);else if Line[0] <> '!'thencall Parms.Dispatch(WindowName, Line);elseremove Escape from Line[0];block declareCmd: Charstring; -- command name following !beginif Line[0] = '!'thencall Parms.Dispatch(WindowName, Line);elseextract cmd from Line[0];select (cmd)where("F")block declareFParm: Charstring; -- parameter to !FbeginFParm := tokenizer(Line);call Parms.Refocus(FParm);call Parms.PutLine( "!wfe: Focus changedto " | FParm | ".");WindowName := FParm;on ( WindowInterface.NotFound)call Parms.PutLine("!wfe:" | FParm | " nota window.");end block;where("K")block declareKParm: Charstring; -- parameter to !KbeginKParm := tokenizer(Line);call Parms.Kill(Kparm);call Parms.PutLine( Kparm | " killed.");on ( WindowInterface.NotFound)call Parms.PutLine("!wfe:" | Kparm | " nota window.");end block;where("Q")running := 'false';

2. A Miniature System 39call Parms.PutLine( "Quitting window manager.");where("C")block declareCParm: Charstring; -- window parameter to!CPName: Charstring; -- program nameParm: Charstring; -- program parametersbeginCParm := tokenizer(Line);PName := tokenizer(Line);block beginParm := tokenizer(Line);on (TokenizeInterface.noToken)Parm := "";end block;call Parms.Create(Cparm, Pname, Parm);WindowName := CParm;on ( CreateInterface.Duplicate)call Parms.PutLine("!wfe: Cannot windowcreate "| CParm|" since it already exists." );on ( CreateInterface.CreateFailure)call Parms.PutLine("!wfe: Unable to createapplication process "|PName|".");end block;otherwise-- error: not F, Q, K or Ccall Parms.PutLine("!wfe: Illegal command:" | cmd);end select;end if;on (TokenizeInterface.noToken)-- errorcall Parms.PutLine( "!wfe: Missing argument.");end block;end if; end if;on (DispatchInterface.NotFound)call Parms.PutLine( "!wfe: Window " | WindowName | "does not exist");end block;end while;on (others)

40 2.5. Front-end Processcall Parms.PutLine( "!wfe: others exception");end block;return Parms;The select statement in the example is the usual abbreviation for com-paring a value against a set of values. It is equivalent to:Temp := Line[ColonPosition + 1];selectwhere(Temp = 'F'). . .where(Temp = 'K') . . .where(Temp = 'C') . . .otherwise . . .end select;Notice that we have separated the code which parses the command fromthe code which obeys the commands. This style of writing has several ad-vantages: (1) It is easy to use the commands with an alternative front-end,e.g. one based upon pointing, or dialog, or menu-selection. This exploitsHermes' ability to plug and unplug modules in di�erent con�gurations. (2)There are tools which automatically generate programs such as the abovefront end from a simple description of the command syntax.The front-end process uses a procedure named Tokenizer to extract atoken (string of contiguous non-blank characters) from a string. Procedures�t very nicely into the process paradigm of Hermes. A procedure is simplya process which is created just for a single call, and which terminates rightafter it returns.Since procedures are so common, Hermes provides a special operation,procedure of to make it easy to invoke procedures. Without it, you wouldhave to issue a create operation before each procedure call.Here is how procedure of works: You code procedure of with a pro-gram operand, just like create of. The result is an output port connectedto an initialization port. However, each time you make a call on that outputport, a new process is instantiated, and your call is given to the initializa-tion port of the new process.If you are not using a procedure re-entrantly or recursively | that is,there exists only one activation at a time, then it doesn't matter whetheryou create a regular process with create or a \process-generator" withprocedure. But if you ever make a second call while the �rst call is stillbeing processed, then it does make a di�erence: if your procedure was imple-mented with create of, the second call will block; if you used procedureof, the second call will not block because a new instance will be createdautomatically.Processes created automatically with procedure of are still like Hermesprocesses in every other way. In particular, they still persist after a returnstatement, unless the return is the last statement. If you want the equiv-alent of Algol procedures, be sure to use procedure of and begin each

2. A Miniature System 41procedure process with a receive and end it with a return.2.6 Tokenizer ProcedureHere is the code of the Tokenizer procedure:receive Parms from Init;pNB := position of C in parms.string where(C <> ' ');extract blanks from B in parms.string where(position of B <pNB);block beginpB := position of B in parms.string where(B = ' ');on (NotFound)pB := size of parms.string;end block;extract parms.token from C in parms.string where(position ofC < pB);return Parms;on (NotFound)return Parms exception NoToken;end block;end processThe program uses some operations on tables which have not yet beenexplained. The table type family is very important, since it is the single Her-mes construct for representing collections of data values. Tables subsumestrings, lists, arrays, �les, trees, associative memories, etc. We have usedtables in a number of examples, but have not explained table operations indetail.Many table operations contain a construct called a selector. A selec-tor has the form: element-variable in table where(predicate). A selectordetermines a subset of elements of a table where a boolean predicate istrue. The predicate is applied to each element of the table.1 The elementvariable is automatically declared to have as its type the type of elementsof the table. The declaration is visible only inside the where expression.For instance, in C in parms.string where(C = ' '), Parms.String is aCharstring, so C is a Char.For ordered tables, you may code the operator position of element-variable within the predicate. The result of position of is the position of1This explanation, and similar descriptions of Hermes semantics, should beunderstood only as a de�nition of program behavior, not as a guide to perfor-mance complexity. Compilers are free to perform optimizations so long as anoutside observer cannot distinguish the optimized and unoptimized programs.Most compilers will not literally implement x in S where(position of x = 7)by scanning each element of S, but instead will use indexing.

42 2.6. Tokenizer Procedurethe element being tested by the selector. For example, element in tablewhere(position of element = expr) means \select the element whoseposition in the table is expr." This form of selector works exactly like arrayindexing. It is so common that Hermes allows you to use the abbrevi-ation table[expr]. We have encountered this abbreviation in the programBoundedBuffer. The statement remove cmd from Line[0] is a shorthandfor remove cmd from C in Line where(position of C = 0), and it means\remove the �rst character from Line and store it in cmd."A selector can appear either by itself as an expression, or it can appearas part of another operator: e.g. remove, every, extract, position:� A selector all by itself in an expression returns a copy of the selectedelement. You can look this operation up in the reference manual underthe name the-element. If there is more than one selected element,then the earliest one is chosen if the table is ordered, and an arbitraryone is chosen if the table is unordered. A NotFound exception is raisedif there is no selected element. Example: Line[0] evaluates to the �rstcharacter of Line, if Line is non-empty, otherwise it raises a NotFoundexception. It can also be written C in Line where(position of C= 0).� The remove statement removes a single element. If no element isselected, a NotFound exception is raised. If more than one element isselected, the �rst selected element is chosen if the table is ordered,otherwise an arbitrary choice is made.� The every of operation generates a new table consisting of everyselected element. If the original table was ordered, the table of selectedelements will have the same relative order. This operation is used tocompute subsets or substrings. (Refer back to the program Compound,where this operation was used to select those characters before andafter the '/'.� The extract statement is similar to every of, except that it removesthe selected elements from its source operand, rather than copyingthe elements.� The position of operation with a selector (e.g. position of C inLine where(C = ':')) returns the position of the �rst selected ele-ment within the ordered table. If no element in the table is selected,a NotFound exception is generated.Note the di�erence between position of selector, which searches a ta-ble, and the position of operator discussed above, which is applied withina selector predicate. The following expression uses both kinds of positionof operators: position of C in Line where(position of C > P and C= ':'). This expression searches for the �rst colon in Line after positionP, and returns the position of that colon.

2. A Miniature System 432.7 The Window Manager2.7.1 DefinitionsNow let's code the window manager process itself. Besides its �ve inputports, and its output port, what state does it need? The window man-ager must know which windows exist, and how to dispatch lines to thosewindows.The natural Hermes way to do this is to de�ne a table. Unlike the tableswe've used so far, this table will be unordered. There will be one element inthe table for each created window. The element will consist of three piecesof information: the window's name, the port over which we dispatch linesdestined for the application in that window, and the Quit port which weneed when we want to kill the window and isolate the client process.We need to de�ne two Hermes types: a record containing the windowname and the two ports, and a table whose elements are records of thistype. Here is what we need to write in WMInt, the de�nitions module fortypes used internally by the window manager:WMInt: using(Standard, Quit) definitionsWindow: record (WindowName: Charstring, -- the name of the windowInputToWindow: PutLineOut, -- port for writing into input bufferQuit: QuitOut); -- port for killing the applicationWindows: table of Window ffullg keys(WindowName);end definitionsThe record de�nition is straightforward. The table de�nition says that atable of type Windows consists of fully initialized records of type Window.If you are familiar with the terminology of relational databases, you willsee that Window de�nes a tuple and Windows a relation. The notationkeys(WindowName) says that no two elements of the table may have thesame value of component WindowName. In relational database terminology,there is a functional dependency between the component WindowName andthe other components.The semantic e�ect of the keys declaration is this: an attempt to inserta record whose WindowName component duplicates the WindowName compo-nent of a record already in the table will fail. The exception DuplicateKeywill be raised.There is also a syntactic e�ect of the keys declaration: the shorthandT[N] is available as an abbreviation for E in T where(E.WindowName =N). This abbreviation exists simply because it's fairly common to search atable on its key components. If the Windows table type had been ordered,you would not be able to use this abbreviation, because it would insteadmean E in T where(position of E = N). The abbreviation for selecting

44 2.7. The Window Manageran element by position takes precedence over the abbreviation for selectingan element by key. Abbreviations are expanded before types are checked,so the fact that N is not an integer will not help.Once again, we remind you that the physical representation of the tableis hidden and in no way a�ects the semantics.2.7.2 SkeletonWe're now ready to write the skeleton of the window manager:WindowManager: using(Standard, WMExternal, WMInt, StartWindowApplication)process (Init: WMIn)declareParms: WMInterface; -- initialization parametersRefocus: WindowIn;RefocusCM: WindowInterface;WriteToWindow: WriteToWindowIn;WriteToWindowCM: WriteToWindowInterface;Dispatch: DispatchIn;DispatchCM: DispatchInterface;Create: CreateIn;CreateCM: CreateInterface;Kill: WindowIn;KillCM: WindowInterface;GetProgram: GetProgramOut;PutLine: PutLineOut;WriteToWindowCapability: WriteToWindowOut;ApplicationBuilder: Program;CreatedWindows: Windows; -- status of all windowsCurrentFocus: Charstring; -- name of window in focusCurrentWindow: Window; -- a window being insert/deleted/searchedbegin-- initialization sectionreceive Parms from Init;GetProgram := Parms.GetProgram;PutLine := Parms.PutLine;ApplicationBuilder := Parms.GetProgram("applicationbuilder");new Refocus; connect Parms.Refocus to Refocus;new WriteToWindow; connect WriteToWindowCapability to WriteToWindow;new Dispatch; connect Parms.Dispatch to Dispatch;new Create; connect Parms.Create to Create;new Kill; connect Parms.Kill to Kill;return Parms;new CreatedWindows;-- service section

2. A Miniature System 45while ('true') repeatselect-- event Refocus ...-- event WriteToWindow ...-- event Dispatch ...-- event Create ...-- event Kill ...otherwiseend select;end while;end processSo far, the window manager has the same structure as servers such asBoundedBuffer. One di�erence is that we do not give the window man-ager's parent a connection to the WriteToWindow service. This connectionwill be given instead to the children of the window manager| the windowapplications.2.7.3 Refocussing and Writing OutputThe Refocus and WriteToWindow services are straightforward. Refocussimply checks whether the window exists.event Refocusreceive RefocusCM from Refocus;if exists of CreatedWindows[RefocusCM.WindowName]thenreturn RefocusCM;else-- error actionreturn RefocusCM exception NotFound;end if;WriteToWindow prepends the window name to the text line.event WriteToWindowreceive WriteToWindowCM from WriteToWindow;call PutLine(WriteToWindowCM.WindowName | ": " | WriteToWindowCM.Line);return WriteToWindowCM;2.7.4 Dispatching Lines to a Particular WindowThe Dispatch service involves a simple table lookup. We return an excep-tion if the destination window does not exist.event Dispatchblock beginreceive DispatchCM from Dispatch;CurrentWindow := CreatedWindows[DispatchCM.WindowName];

46 2.7. The Window Managercall CurrentWindow.InputToWindow(DispatchCM.Line);return DispatchCM ;on(NotFound)return DispatchCM exception NotFound;end block;This program illustrates one way to look up something in a table|use aselector expression to copy an element from the table into a variable. Weshould point out one situation in which this does not work. The input portand callmessage type families are not copyable. Variables of these types, orof any other type containing these types (such as a record containing aninput port or a table containing a callmessage) cannot be copied. To do atable lookup when the table elements cannot be copied, use the inspectstatement. Here is the same program fragment rewritten with the inspectstatement.block beginreceive DispatchCM from Dispatch;inspect W in CreatedWindows[DispatchCM.WindowName]begincall W.InputToWindow(DispatchCM.Line);end inspect;on(NotFound)end block;The inspect statement assigns W from a constant copy of the selectedelement of table CreatedWindows. Hermes does not allow making copiesof input ports and callmessages, because the semantics of receive andreturn would become complex. However constant copies may be made ofan object of any datatype, because the problematic operations are illegalwhen applied to constants.In the program fragment written without inspect, it is necessary todeclare CurrentWindow. But in the version using inspect, you don't writea separate declaration for the variable W. The inspect statement de�nes anew scope in which W is declared to have type Window|the element typeof table CreatedWindows. You may wish to use the inspect statementfor table lookup all the time, even when the table elements are copyable.That way, you will not have to use two di�erent styles for the copyable andnon-copyable cases.The inspect statement selects exactly one element (or else raises anexception). To select all elements, iteratively, use a for statement. Thefor statement iteratively executes the statements within its body once foreach element of a table which satis�es the selector predicate. (This may bezero times if the table is empty or if no element satis�es the selector.) Forexample, here is a piece of code which prints the names of all the activewindows:for Window in CreatedWindows where('true')

2. A Miniature System 47inspectcall PutLine(Window.WindowName);end for;Like the inspect statement, the for statement creates a scope. There-fore you do not declare Window explicitly. Within that scope, the variableWindow contains a constant copy of one of the selected elements of the ta-ble. For ordered tables, the iterations occur in the order that the selectedelements occur in the table. For unordered tables, the iterations occur inan arbitrary order.The construct where('true') may be abbreviated as [ ]. This abbre-viation is particularly useful in the for statement.2.7.5 Creating and Killing WindowsNow let's do creation and destruction of windows. To create a window,we're going to build a new window record, and insert it into the table.The new window record contains the window's name, and the two portsInputToWindow and Quit. We get these ports by creating an applicationbuilder process. The application builder will create the three applicationprocesses, and return the two desired ports. We have to pass the appli-cation builder these parameters: the program name and parameter string,a connection to GetProgram, and a connection to WriteToWindow. Thecode is very straightforward now that we have all the interfaces and datastructures de�ned:event Createreceive CreateCM from Create;if exists of CreatedWindows[CreateCM.WindowName]then-- error action because window already existsreturn CreateCM exception Duplicate;elseblock beginnew CurrentWindow;CurrentWindow.WindowName := CreateCM.WindowName;call ( WindowApplicationOut # (create of ApplicationBuilder))(CreateCM.ProgramName, CreateCM.ParmString,GetProgram, WriteToWindowCapability, CurrentWindow.WindowName,CurrentWindow.InputToWindow, CurrentWindow.Quit);insert CurrentWindow into CreatedWindows;return CreateCM;on (WindowApplicationInterface.NotCreated)-- error action if unable to create processreturn CreateCM exception CreateFailure;end block;

48 2.7. The Window Managerend if;The only tricky bit is that we must avoid the possibility of a DuplicateKeyexception by checking �rst for the existence of a window with identicalname. If we waited to build the application �rst and then check for aDuplicateKey exception, the illegal application may have already startedrunning and may have already produced output. The exists of operatorchecks for this case. This operation takes a selector and evaluates to trueif and only if one or more selected elements exist.To destroy a window, we simply remove the window descriptor from thetable of created windows and then call the Quit port associated with thatapplication. This service will call Quit on the bu�er process and the adapterprocess, with the result that the client process will shut down.event Killreceive KillCM from Kill;block beginremove CurrentWindow from CreatedWindows[KillCM.WindowName];call CurrentWindow.Quit();discard CurrentWindow;return KillCM;on (NotFound)-- error actionreturn KillCM exception NotFound;end block;event GetFocusreceive GetFocusCM from GetFocus;if CurrentFocus <> ""thenGetFocusCM.Focus := CurrentFocus;return GetFocusCM;elsereturn GetFocusCM exception NotFound;end if;This �nishes the window manager. Notice how convenient it is to writethis code when you have tables, exception handling, and the ability to passports from process to process.2.8 Creating a Window ApplicationAll that is left are the application builder, the adapter, and the quit dis-patcher. These programs use no new principles. If you can build Compoundor WMSystem, it should be easy to build ApplicationBuilder.And Adapteris a straightforward variation of Filter.

2. A Miniature System 492.8.1 Application BuilderHere is the code of ApplicationBuilder. It creates an instance of theunbounded bu�er, an instance of the adapter, and an instance of the client.It also builds a process which interprets the Quit callmessage by callingthe Quit service in both the bu�er and the adapter.Here is the only tricky part: ApplicationBuilder trusts the bu�er, theadapter, and the quit handler processes. It doesn't trust the client, sincethis might be an arbitrary module, which might not even return from itsinitialization call. So ApplicationBuilder should be sure to call the clientlast. If the client goes into a loop or deadlocks, we are still safe, becausethe window manager will still be running. The end user can then kill thewindow. This will cause the quit dispatcher program to call Quit in thebu�er and the adapter. When the bu�er and the adapter terminate, theclient and the application builder processes become isolated, and they willbe garbage collected.ApplicationBuilder:using(Standard, StartWindowApplication, BBExternal, Adapter,Quit,QuitDispatcher)process (Init: WindowApplicationIn)declareParms: WindowApplicationInterface; -- initialization callmessageClient: StandardOut; -- client processParmString: Charstring; -- parameter to client processClientPutLine: PutLineOut; -- PutLine from Client to AdapterClientGetLine: GetLineOut; -- GetLine from Client to BufferClientGetPgm: GetProgramOut; -- GetProgram from Client toAdapterbeginreceive Parms from Init;blockdeclareAdapterQuit: QuitOut; -- adapter's quit portBufferQuit: QuitOut; -- buffer's quit portbegincall (AdapterOut # (create of Parms.GetProgram("adapter")))(Parms.WriteToWindow, Parms.WindowName, Parms.GetProgram,AdapterQuit, ClientPutLine, ClientGetPgm);call (BBout # (create of Parms.GetProgram("unboundedbuffer")))(0, Parms.InputToWindow, ClientGetLine, BufferQuit);call (QuitDispOut # (create of Parms.GetProgram("quitdispatcher")))(AdapterQuit, BufferQuit, Parms.Quit);Client := create of Parms.GetProgram(Parms.ProgramName);ParmString := Parms.ParmString;

50 2.8. Creating a Window Applicationreturn Parms;block begincall Client(ClientGetLine, ClientPutLine, ClientGetPgm,ParmString);on (others)end block;on (others)return Parms exception NotCreated;end block;end processNote the on (others) exception handler. This is a shorthand notation,signifying \handle all exceptions which are not already explicitly handled".In this example, this handler will be entered if anything goes wrong tryingto create the client. The handler will return a NotCreated exception. Oncethe client is created, we don't want exceptions returned by the client itselfto cause a NotCreated exception. Therefore, we enclose the call to theclient within an inner block with a null on (others) handler.Since the code of the quit dispatcher, adapter, and bu�er are known atcompile-time, we need not call GetProgram to load these programs fromthe program library at run time. We do this using a program literal.A literal is an expression with a compile-time known value. We have usedstring literals, like "Hello, World!", integer literals, like 0, and booleanliterals, like 'true'. These literals are just like the literals of familiar lan-guages, so we didn't bother explaining them.A program literal is just program text delimited by process and endprocess. For example, suppose that instead of loading QuitDispatcherfrom the library, we used a program literal instead. Here is the relevantfragment of module ApplicationBuilder:call (QuitDispOut # (create of process (Init: QuitDispIn)declareParms: QuitDispInterface;Quit1: QuitOut;Quit2: QuitOut;Quit: QuitIn;QuitCM: QuitInterface;beginreceive Parms from Init;new Quit; connect Parms.Quit to Quit;Quit1 := Parms.Quit1;Quit2 := Parms.Quit2;return Parms;receive QuitCM from Quit;call Quit1();call Quit2();

2. A Miniature System 51return QuitCM;end process))(AdapterQuit, BufferQuit, Parms.Quit);Don't confuse program literals with static bindings, which Hermes doesn'thave. As we stated early in the tutorial, everyone with a static binding toprintf has access to the same console. But if you and I each have a pro-gram literal denoting the QuitDispatcher program, we will still not haveaccess to the same process. If we each instantiate the program, we'll havetwo separate processes running the same code. The only way we can sharea process is if one of us instantiates the process, and passes a copy of theoutput port to the other.Don't confuse program literals with nested procedures in block struc-tured languages. In block structured languages, nested procedures can re-fer to variables in the outer procedure. This is not true here. A programliteral is a completely separate program with a completely separate setof declarations. The program literal is treated as if it had been compiledas a separate module. It evaluates to an initialized, checked value of typepredefined!program.You may write a program literal out of line, separately compile it, andrefer to it by name within another program. This is convenient if the sameprogram text appears as a program literal in several programs. You don'twant to maintain multiple copies of the same program literal, in case youwish to modify the program.Here is the same code with the program literal out of line:linking (QuitDispatcher). . .call (QuitDispOut # (create of process QuitDispatcher))(AdapterQuit, BufferQuit, Parms.Quit);An out-of-line program name is made visible by means of a linking list,which appears right after the imports list at the beginning of the sourcemodule. In this example, we make the name QuitDispatcher visible bywriting linking(QuitDispatcher).2.8.2 Adapter and Quit DispatcherTest yourself trying to write the adapter and quit dispatcher processes.Here is the code:Adapter: using(Standard, Adapter, WMExternal, Quit)process(Init: AdapterIn)declareParms: AdapterInterface; -- initialization callmessagePut: PutLineIn; -- PutLine serviceGet: GetProgramIn; -- GetProgram serviceQuit: QuitIn; -- Quit service

52 2.8. Creating a Window ApplicationWindow: WriteToWindowOut; -- access to windowGetProgramOut: GetProgramOut; -- access to GetProgram serviceWindowName: Charstring; -- name of windowRunning: boolean; -- true if process has not been shut downGetProgramCM: GetProgramInterface;PutCM: PutLineInterface;QuitCM: QuitInterface;begin-- initializationreceive Parms from Init;new Put; connect Parms.ClientPutLine to Put;new Get; connect Parms.ClientGetProgram to Get;new Quit; connect Parms.Quit to Quit;Window := Parms.AdapterToWindow;GetProgramOut := Parms.GetProgram;WindowName := Parms.WindowName;Running := 'true';return Parms;-- service loopwhile (Running) repeatselectevent Putreceive PutCM from Put;call Window(WindowName, PutCM.Line );return PutCM;event Getreceive GetProgramCM from Get;call GetProgramOut(GetProgramCM.Name, GetProgramCM.TheProgram);return GetProgramCM;event Quitreceive QuitCM from Quit;Running := 'false';return QuitCM;otherwise -- should not occurend select;end while;end processQuitDispatcher: using(QuitDispatcher, Quit) process (Init: QuitDispIn)declareParms: QuitDispInterface;Quit1: QuitOut;Quit2: QuitOut;

2. A Miniature System 53Quit: QuitIn;QuitCM: QuitInterface;beginreceive Parms from Init;new Quit; connect Parms.Quit to Quit;Quit1 := Parms.Quit1;Quit2 := Parms.Quit2;return Parms;receive QuitCM from Quit;call Quit1();call Quit2();return QuitCM;end process2.9 SummaryIn this section, we have built a complete miniature system. The system issimpli�ed, but it illustrates many features of real systems. For example,it is a functional enhancement|windows|which can operate with clientprograms which were designed prior to the enhancement. It is transparent|that is, the client programs do not need to be changed to use windows.It achieves this transparency by exploiting Hermes' ability to rebind anyoutput port to an input port of matching interface. Furthermore, becausethe window system as a whole looks like a standard client, we can run thewindow system itself within one of its own windows to get nested windows.The application builder illustrates access control. The client is passed anoutput port to an adapter which calls a particular window|therefore theclient can be constrained to write only into the appropriate window. Andthe window manager can revoke an errant client's access to the window byterminating the adapter.The window manager illustrates how systems add or delete services dy-namically. In this case, client applications and the ports to them are addedand deleted.This example illustrates how we divide the responsibility and knowledgeamong the processes of a system. The front end deals with input from theend user. It does string handling, and converts string commands to calls.The window manager handles the set of window applications|it primarilydeals with the table of active windows. The adapter and bu�er processesconvert between di�erent interfaces, and also allow a service to be cuto�. The window system and application builder processes load and linktogether other processes.If you understand how the window manager system is built, you under-stand the essential concepts and uses of Hermes. It should be a straightfor-

54 2.9. Summaryward exercise to write more complex systems by applying the same prin-ciples. For example, you may want the window system to support a newstyle of clients, which have access to the Create service and can create newwindows for their child processes to run in. You may want more realisticerror handling. You may wish to try writing improvements on some of theexamples illustrated here.You also may wish to think of how you would have written these pro-grams in your favorite programming language. Don't forget that the pro-grams must be robust|there must be no crashes due to exceeding a stor-age limit, for example. The programs must also be safe|clients must notbe able to write on windows they don't own. You have to close all theloopholes|like opening /dev|which might circumvent the security of thesystem. The system may be running for a long time|so you must makesure not to forget to free unused dynamically allocated storage. It wouldbe instructive to estimate how much thinking you have to do to ensurea correct implementation in another language and compare it to what isinvolved in doing the job in Hermes.Summary: In this section, we have mostly applied what we've learnedin previous sections to a more practical problem. We have introduced afew new constructs: record type de�nitions, unordered tables, keys, selec-tors, every of, extract, position of (with a selector and with a se-lected element), the inspect statement, the DuplicateKey exception, theon (others) notation. From here on, you have nothing more to learn aboutHermes except a more complete list of primitive operations, and a more re-�ned description of what has been discussed informally. Good luck!

3Type and Typestate CheckingC.A.R. Hoare, in his 1981 Turing Award Lecture, \The Emperor's OldClothes", called upon language designers to enforce what he called securityin programming languages, and criticized the recently adopted Ada designfor not meeting this requirement. By security, Hoare meant \[t]he principlethat every syntactically incorrect program should be rejected by the com-piler and that every syntactically correct program should give a result oran error message that was predictable and comprehensible in terms of thesource language program itself." In a secure language, a bug in module Xshould show up as bad output from module X, not as a crash, or a randomimplementation-dependent perturbation to a possibly innocent module Y.Security in Hoare's sense is extremely important for a language like Her-mes. Programs are long-lived systems, they include modules written bymultiple users, they may dynamically load arbitrary untested programs,and they must keep on running the good programs even though some badprograms have been allowed to execute.Let's adopt Ada's terminology and call executions which violate securityerroneous executions. Erroneous executions are unchecked violations of lan-guage rules. (We're that the language has some concept of modularity inwhich it is illegal for one module to access the private data of another.)In practice, erroneous executions are the result of: (1) accessing unde�nedvalues, or (2) accessing data of the wrong type.Conventional type checking avoids most of the problems of accessingdata of the wrong type. The remaining security problems in languages likeAda are the result of aliasing and shared data. For example, two processessimultaneously write into a single variable, and the result is a possiblyinterleaved succession of bytes from both values, which may not be a well-formed value of the appropriate type. Or one process modi�es a variantafter another has already checked its discriminant. Since Hermes has nosharing and no aliasing, these pathological cases cannot arise.The Hermes type system is straightforward. Every variable name hasa type which is �xed throughout its scope, and which is directly derivedfrom the declaration. Every operation has class rules, which limit whichfamilies of types the operands may have|for example, + may only be usedwith integer or real types. Every operation has type inference rules, whichdetermine the type of one operand as a function of the type of another|forexample if S is a Charstring, de�ned as ordered table of Char, then ininsert C into S, C must be of type Char. The declared types of variablesand the inferred types of expressions must (1) determine a unique type for

56 3. Type and Typestate Checkingevery expression and (2) be consistent with the type rules.The problem of unde�ned values is solved in Hermes by typestate check-ing | a generalization of data ow analysis.. Hermes compilers use types-tate checking to track where a variable is uninitialized (and therefore maynot be read), where a variant is in the wrong case (and therefore may nei-ther be read nor written), and where a program value is unchecked (andtherefore may not be instantiated). A Hermes compiler will reject a programwhose Pascal or Ada counterpart would compile successfully but producean erroneous execution.Here's an example of typestate checking applied to a simple (and rathermeaningless) program fragment:blockdeclareL: Charstring;A: Char;B: Charstring;C: Charstring;begincall Parms.GetLine(L);if size of L = 0thenA := 'NUL';B := "Empty Line";call Parms.PutLine(B);elseA := L[0];call Parms.GetLine(C);call Parms.PutLine(C);end if;insert A into L;call Parms.PutLine(L);call Parms.GetLine(L);on (GetLineInterface.EndStream)call Parms.PutLine(L);end block;For now, let us look only at the simplest application of typestate |keeping track of which variables are initialized: A typestate is representedas a set of attributes describing properties of variables, such as whethera variable is initialized or uninitialized. If variable A is initialized, the at-tribute init(A) is present in the typestate; otherwise the attribute is ab-sent. Typestate analysis computes a typestate for each program point. Thismeans that at a given program point variable A must be known to be eitherinitialized or uninitialized. We do not allow A to be initialized along somepaths and not others.

3. Type and Typestate Checking 57Let us follow the analysis of typestates in the above example. To simplifythe discussion, we will look only at typestate attributes relating to theinitialized state of variables A, B, C, and L.On entry to the block, none of the variables is initialized, so the typestateis empty. After the call to GetLine, the typestate is finit(L)g|only L isinitialized. At the end of the then clause, after the assignments to A and B,the typestate is finit(L), init(A), init(B)g. At the end of the elseclause, the typestate is finit(L), init(A), init(C)g.At the end of the if statement, the two paths merge. The typestateat the merge point is computed as the intersection of the typestates. 1 Inthis case, that typestate is finit(L), init(A)g|variables L and A willbe initialized, B and C uninitialized.Here is where typestate analysis goes beyond data ow analysis. In data owanalysis, you always know less information at a merge point than at eitherentry to the merge. So all you would know about B and C would be that theywere possibly initialized, and possibly not. In Hermes, we do not wish thereto be run-time tests of initializedness|a variable must either be known tobe initialized or known to be uninitialized. The intersection rule impliesthat B and C will be uninitialized at the merge point. To make sure that Band C are in fact uninitialized at this point regardless of the execution pathtaken, the compiler inserts coercion operations. In this case, the compilerwill insert a discard B statement after the then clause and a discard Cstatement after the else clause.A typestate with a strict subset of the attributes of a second typestateis said to be lower. Coercion operations always convert from a higher type-state to a lower one.A similar situation prevails at the exception handler on(GetLineInterface.Endstream).There are three paths to that handler|one from each call to GetLine. Thethree typestates are fg (�rst call), finit(L), init(A)g (second call), andfinit(L)g (third call). The intersection is the typestate fg. The compilerautomatically inserts operations to discard L and A into the path from thesecond call to GetLine to the handler, and it inserts an operation to discardL into the path from the third call.Typestate analysis not only tracks the typestate and performs coercions,it also checks for legality of programs. For instance, at the insert state-ment, it is checked that both L and A are initialized. In the statement callParms.GetLine(A), the GetLineIn de�nition requires the argument to beuninitialized, yet A is initialized. Here, the typestate is too high. A coercionoperation discard A is inserted so that A will be uninitialized as expected1Mathematically, the set of typestates is a semilattice|that is, a mathematicalstructure where the elements (typestates in this case) are partially ordered, andwhere every pair of elements has a meet or greatest lower bound. In Hermes, onetypestate is lower than the other if it is a subset of the other; the meet of twotypestates is their intersection.

58 3. Type and Typestate Checkingbefore the call. In the statement call Parms.PutLine(L) in the handler,L is required to be initialized, and it is not, so the typestate is too low.There is no coercion to make a typestate higher, so this statement must berejected as erroneous. The compiler will issue an error message saying thatattribute init(L) was required but not present.What bene�ts do we get from typestate checking? First, we get security.Allowing a statement like call Parms.PutLine(L) to execute with an un-de�ned L could produce unpredictable results|possibly even a programtrap. (Imagine L implemented as a pointer to a string bu�er.) This wouldbe fatal, especially if our program is a multi-user system. And if we didn'tdetect the error at compile-time, we'd have to check for it at run-time.Second, we get early detection of nonsense programs. Third, we get e�-ciency. When programs are written in unsafe languages, each user has tobe put in a separate address space so that one user's erroneous programcan't hurt another user. But we can run multiple Hermes processes in oneaddress space. Because the implementation is secure, we get the protectionof address spaces at the run-time cost of lightweight processes. And onmachines lacking memory mapping, we get protection which we would nototherwise have. Finally, we get free garbage collection in the form of auto-matically generated discards of values. For instance, we saw that when theEndStream exception was raised, the appropriate variables were discardedbefore entering the exception handler.What price do we pay? You have to declare the pre-call and post-calltypestates of call parameters on interfaces. This can be viewed as either aburden or a bene�t depending upon your point of view. If you're picking upsomeone else's modules, the extra documentation on the interface is useful.If you're writing a procedure which no one but yourself is going to use andyou can keep the interface \in your head", you may consider writing extradeclarations a burden. Hermes is designed primarily for large, long-livedsystems, so it favors the �rst point of view over the second.

4Additional Hermes ConstructsThe examples in chapters 1.1 and 2 illustrate most of the novel Hermesconstructs. However, there are some useful Hermes constructs which havenot yet been discussed, so we will brie y discuss them here:4.1 Expression BlocksHermes is a statement-oriented language. However, expressions are so con-venient and so widely used that it would be foolish to bar them fromHermes for the sake of uniform syntax. An expression is an operation orseries of operations which compute a single value which is immediately usedas the operand of another statement or expression. All the operands of anexpression except the result are unmodi�ed by the expression.Sometimes it is desirable for an expression to include embedded state-ments. We support this in Hermes with an expression block. We'll illustratethis by rewriting the Create service of the window manager.receive CreateCM from Create;if exists of CreatedWindows[CreateCM.WindowName]then-- error action if window already existselseblock begininsert(evaluate W : Window fromnew W;W.WindowName := CreateCM.WindowName;call (WindowApplicationOut # (create of ApplicationBuilder))(CreateCM.ProgramName, CreateCM.ParmString,GetProgram, WriteToWindowCapability, W.WindowName,W.InputToWindow, W.Quit);end)into CreatedWindows;on (WindowApplicationInterface.NotCreated)-- error action if unable to create processend block;end if;return CreateCM;

60 4.1. Expression BlocksIn this version of the program, the block consists of a single insertstatement. The operand of insert is the record CurrentWindow which isbuilt within the expression block. In this particular case, the expressionblock wasn't really necessary. The original version was just as convenient.However, in some contexts, such as after where in a selector, you must usean expression block if you need to write a statement.4.2 SendAnother common idiom is to send some data to another process and notwait for a reply. You can do this by de�ning an interface in which the datayou wish to send is initialized on entry and uninitialized on exit. However,there is still the problem that the receiving process may choose to waitinde�nitely before receiving your data. It may even run forever and neverreceive your data.A possible Hermes solution is this: the sending process, S creates anintermediate process I. S calls I, which immediately receives the data,and returns to S. Then I calls the eventual receiver R. If R is delayed ordeadlocked, it is I who is blocked and not S.Rather than requiring the programmer to explicitly code an intermediateprocess, Hermes provides an asynchronous communicationmechanism. Thestatement send variable to port causes the value currently in variable tobe removed and sent to the queue at the input port connected to port.Because of the send operation, input ports can be de�ned to hold anydatatype, not just callmessages.Here are two practical uses of send:� Communicating with untrusted processes: Suppose I wish to load andexecute a process and pass it some initialization information|possiblysome ports. I could pass this information with a call statement, justas we did in earlier examples. However, there is some risk to doingthis if I do not trust the process I am loading. Perhaps the processwill never return my callmessage, and I will block forever. To be safe,I either must spawn a process to initialize and call the untrustedprocess, or I must deliver the initialization parameters using a sendstatement.� Forwarding calls: Suppose I am writing a process whose job it is toreceive calls and route them to an appropriate process which will ac-tually service the call. If my process receives the callmessage and thencalls the eventual destination, it must wait until the call is serviced.However, if instead my process transfers the callmessage using send,my process is free to route other calls. When the eventual destinationissues a return statement, the callmessage will be returned to theoriginal caller, and not to my process.

4. Additional Hermes Constructs 614.3 VariantsSometimes the requirement that the type of every value be known atcompile-time is too constraining. Hermes supports variants and polymorphsto allow this requirement to be weakened. However we do not give up thefundamental principles that machine-level implementation detail must behidden, and that the language must be secure.We illustrate variants with an example which will be familiar to anyonewho has programmed in LISP. A LISP object is either an atom (whichfor purposes of this example, we'll assume to be just a character stringprintname), a pair, or nothing (nil). Here are the Hermes types which letus talk about LISP objects:LispKind: enumeration ('atom', 'pair', 'nil');LispPair: record(Car: LispObject, Cdr: LispObject);LispObject: variant of LispKind ('atom' -> PrintName: Charstring ffullg,'pair' -> Pair: LispPair ffullg,'nil' -> Nil: Empty fg);The enumeration and record types are just like similar types in otherlanguages. Empty is a prede�ned type|an enumeration with no values|which is used conventionally to represent a null value. We'll explain thevariant type.Suppose L is a variable of type LispObject. Then L has three compo-nents: L.PrintName of type Charstring, L.Pair of type LispPair, andL.Nil of type Empty. In this respect, a variant is just like a record. Butwhereas an initialized record may have several components, only one com-ponent of a variant can exist at any one time. Which component it is de-pends upon the case of the variant. The case is an enumeration value|inthis example, either 'atom', 'pair', or 'nil'. Each component is asso-ciated with one of the cases of the variant. So the value of an initializedvariant is a case value plus exactly one of the variant components.An initialized variant can be either hidden or revealed. Hidden meansthat we don't know the case at compile time. Revealed means we do. Thisis a typestate property. That means that the compiler will keep track ofwhere in the program the variant is hidden, and where it is revealed. Whenit is revealed, the compiler tracks its case. So if L is a LispObject, thepossible typestates of L are:� uninitialized� initialized and hidden� initialized and revealed in case 'atom'� initialized and revealed in case 'pair'

62 4.3. Variants� initialized and revealed in case 'nil'There are two ways to initialize an uninitialized variant. You can use anassignment statement to copy the value of another variable of the sametype which is already initialized. Or you can build a new variant. Whenyou build a new variant, you must decide which case to put the variantin. You may not directly assign to a component of an uninitialized variant.You must instead use the unite statement to move a value into one ofthe variant components. The unite statement simultaneously: (1) sets thecase of the variant, (2) moves the value of its operand into the variantcomponent, (3) tells the compiler that the variant is now initialized andrevealed.1. Here is an example of a statement which initializes L into case 'atom':unite L.Printname from "Foo";2. This statement puts L into case 'nil':unite L.Nil from Emptyval;(Emptyval is a variable of type Empty. It doesn't need to be initialized,since in the de�nition, component Nil is expected to be uninitialized.)3. This statement puts L into case 'pair':new Pair;unite Pair.Car.Printname from "Foo";unite Pair.Cdr.Nil from Emptyval;unite L.Pair from Pair;The typestate attribute case is present when the variant is revealed. Forexample, when variant L is revealed in case 'atom', the attribute case(L,L.PrintName) is present. This means that L.PrintName is the componentwhich is known to exist.When you write the de�nition of the variant type, you have to supplya typestate for each variant component. This is the typestate the variantcomponent will have (1) just before it becomes hidden, and (2) just after itis revealed. It is also the minimum typestate the component can have anytime it is revealed | that is, it may be made more initialized but not less.The de�nition of LispObject illustrated above shows that L.PrintNamemust be fully initialized when L is in case 'atom', L.Pair must be fullyinitialized when L is in case 'pair', but L.Nil need not be initialized atall when L is in case 'nil'.`The typestate after each of the three unite statements above is respec-tively:1. finit(L), case(L, L.PrintName), init(L.PrintName)g

4. Additional Hermes Constructs 632. finit(L), case(L, L.Nil)g3. finit(L), case(L, L.Pair), init(L.Pair), init(L.Pair.Car),case(L.Pair.Car, L.Pair.Car.PrintName), init(L.Pair.Car.PrintName),init(L.Pair.Cdr), case(L.Pair.Cdr, L.Pair.Cdr.Nil)gNotice that in case 'pair', L.Pair.Car and L.Pair.Cdr are themselvesvariants of type LispObject. The de�nition requires that when L.Pair isrevealed, both L.Pair.Car and L.Pair.Cdr must be fully initialized. Inthe example, they are both initialized and revealed. This is legal, sincefully initialized is the minimum typestate, and initialized and revealed isa higher typestate. It would be illegal to perform an operation to makeL.Pair.Car uninitialized while L.Pair is revealed.If the case attribute is not present, then the variant is hidden. You mayhide a variant explicitly with the hide statement. But usually variants be-come hidden because of a coercion. For example, if you wrote a programwhich took three di�erent paths, each executing one of the unite state-ments mentioned above, then at the merger of the paths the typestatewould be finit(L)g.When the variant is initialized but hidden, you may not access any ofthe components, because only one of them really exists and you don't knowwhich one it is. For example, suppose the typestate is finit(L)g and youtry to do the assignment L.Printname := "Foo". This will generate a type-state error message saying \adding attribute init(L.PrintName)would re-sult in impossible typestate". The typestate finit(L), init(L.PrintName)gis impossible because you may not have any attribute of a variant compo-nent without the appropriate case attribute.To reveal a variant, use the reveal statement. The operand of reveal isthe component you hope to access. If the component does not exist becausethe variant is in the wrong case, you will get a CaseError exception. Ifyou want to know the case of a variant so you know which component it iscorrect to reveal, issue the case of operation. This returns the enumerationvalue of the case. Here's a fragment of a procedure which swaps Car andCdr of a LispObject to arbitrary depth:receive Parms from init;block beginreveal SwapCM.L.Pair;Temp := Swap(SwapCM.L.Pair.Car); -- it's a pairSwapCM.L.Pair.Car := Swap(SwapCM.L.Pair.Cdr);SwapCM.L.Pair.Cdr := Temp;on (CaseError) -- it's not a pairend block;return Parms;After the reveal, the component of the revealed variant has the typestateattributes which the de�nition said it was supposed to have. You may raise

64 4.3. Variantsthe typestate (i.e. add attributes), but you may not lower any attributes.Furthermore, any attributes you added will be coerced away before thevariant is hidden again. This guarantees that when the variant is revealedagain, it will have a known typestate regardless of what you did. So in theabove example, Parms.L.Pair will be fully initialized, since the de�nitionof LispObject said that in case 'pair', component Pair is full. Thismakes the typestatefinit(Parms.L), case(Parms.L, Parms.L.Pair),init(Parms.L.Pair), init(Parms.L.Pair.Car), init(Parms.L.Pair.Cdr)gIf you wrote discard Parms.L.Pair.Car, this would be illegal. Youwould get a compiler error message saying that attempting to drop init(Parms.L.Pair.Car)would yield an impossible typestate. If you raise the typestate, for exam-ple by issuing reveal Parms.L.Pair.Car.PrintName, the typestate willbe lowered back automatically as soon as you try to hide Parms.L.4.4 PolymorphsA variant is useful when you know ahead of time a �xed set of alternativetypes (or typestates) a value will have. Sometimes there is an open-endedset of alternatives. In such a case, you have to use a polymorph instead ofa variant.A polymorph is a variable which can hold any type of value. The valueis always stored together with its type and typestate. Think of the valueas being wrapped in an opaque wrapper labelled with the type and types-tate. When the value is wrapped, it can be stored in a polymorph variable,inserted into a table of polymorphs, etc. But none of the operations of theoriginal type are allowed. In order to issue these operations, the polymorphmust be unwrapped and its value stored back into a variable of the ap-propriate type. At this time, there is a run-time check that the type andtypestate you are expecting match the type and typestate appearing onthe wrapper.The only operations you may perform on a polymorph besides wrappingand unwrapping are those operations allowed on all types, such as insertinginto a table. It is syntactically legal to copy a polymorph, but you will getthe exception Uncopyable if the wrapped value is one of the uncopyabletypes. You may also inspect the wrapper to read the type and typestate ofthe wrapped value.Here is a simple application of polymorphs|a resource repository intowhich the owner of a resource (e.g. a port into a server) may post a copyof the resource and a resource name, and authorized users may retrievea copy of that resource by name. Since di�erent resources are of di�erenttypes, the interface to the repository cannot specify any particular type.Instead we require the owner to wrap the resource into a polymorph before

4. Additional Hermes Constructs 65posting the resource. The requestor of a resource supplies a resource nameand receives back a polymorph, which the requestor must then unwrap.The owner of a resource may not wish to allow any arbitrary user togain access. To provide access control, we require the owner to provide aport into an access control function. The access control function accepts auser name and the resource originally posted, and then either returns theresource being granted to the user, or returns a Denied exception. 1Here is the interface:RepositoryExternal: using() definitionsResource: polymorph;PostInterface: callmessage(ResourceName: Charstring, -- name being given to resourcePostedResource: Resource, -- resource being postedAccessFunction: AccessOut) -- access control functionconstant(ResourceName, AccessFunction)exit ffullgexception DuplicateName ffullg;PostIn: inport of PostInterface ffullg;PostOut: outport of PostIn;AccessInterface: callmessage(UserName: Charstring, -- name of the requestorPostedResource: Resource, -- resource originally postedReturnedResource: Resource) -- the resource grantedconstant(UserName, PostedResource)exit ffullgexception AccessDenied finit(UserName), init(PostedResource)g;AccessIn:inport of AccessInterface finit(UserName),init(PostedResource)g;AccessOut: outport of AccessIn;RequestInterface: callmessage(ResourceName: Charstring, -- name of resource being requestedResource: Resource) -- the resource grantedconstant(ResourceName)exit ffullgexception NotFound finit(ResourceName)gexception AccessDenied finit(ResourceName)g;RequestIn: inport of RequestInterface finit(ResourceName)g;RequestOut: outport of RequestIn;1The resource returned may or may not be the same as the resource posted.The access control function may decide to grant limited access to the resourcethrough a �lter, for example, in which case it will return a port into the �lterrather than the port into the resource.

66 4.4. Polymorphs. . . -- other interfacesend definitionsHere's a program fragment which posts the GetLine service with trivialaccess control { anyone can get it.Trivial := procedure of process (Init: AccessIn)declareParms: AccessInterface;beginreceive Parms from Init;Parms.ReturnedResource := Parms.PostedResource;return Parms;end process;wrap copy of GetLine as Resource;call Post("GetLine", Resource, Trivial);The wrap statement removes the value of its source operand before wrap-ping it. If you want to use GetLine after the wrap statement, be sure towrap a copy.Here is the code to obtain the GetLine port which someone else hasposted:block begincall Access("GetLine", Resource);unwrap GetLine from Resource finitg;. . .on(PolymorphMismatch)on(others)end block;The PolymorphMismatch exception will be raised by the unwrap state-ment if the type of the wrapped value of Resource doesn't match thetype of variable GetLine, or if the typestate of the wrapped value is lowerthan finitg. These checks are necessary to preserve security. Without thechecks, you could wrap a value supporting one set of operations and thenlater unwrap it and perform di�erent operations.

5Research Directions in HermesIf you have followed the examples so far, you should be reasonably uentin Hermes.We believe that Hermes is an excellent technology for large system devel-opment. It combines expressive power, simplicity, portability, and safety,merging the best features of many language paradigms: Like object-orientedlanguages, Hermes supports information hiding and program reuse|butunlike object-oriented languages, there is support for concurrency and ac-cess control ([NS88, Str86]). Like LISP, Hermes allows running programs tobuild other programs, but unlike LISP, Hermes will check these programsfor type errors before execution. Like Ada and Modula, Hermes supportsmodularity and interface speci�cation, but unlike these languages, Hermesprovides complete security using typestate checking. Like C/UNIX, Her-mes supports systems programming, but uses a simpler and completelymachine-independent set of primitives to do it. And Hermes provides thefamiliar sequential programming paradigm of the imperative languages.As of 1990, the Hermes project at IBM Research is using Hermes to ex-plore newer approaches to simplifying the development of complex systems:� A more re�ned semilattice structure, which allows programmers totrack more information about the value of variables, and which wouldallow additional coercions ([SY89, SY90]).� Transparent recovery, which makes it unnecessary for programmersto treat process state as volatile and do their own checkpointingand restart ([SY85, SY84, YSB87, SBY88, SBY87, SYB87c, SYB88,FBSY87, SYB87d]).� Optimistic transformations, which perform concurrency control ([Jef85]),process replication ([GJ87]), call-streaming ([BS90]), and paralleliza-tion transparently ([SY87]). Such optimizations make it unnecessaryto code these complex protocols by hand. Straightforward but su-per�cially ine�cient programs, such as a server with thousands ofclients coded as a single serial process, are transformed into e�cientimplementations on parallel and distributed architectures.

68 5. Research Directions in Hermes

Part IIHermes ReferenceManual

69

6IntroductionThe reference manual presents the rules of Hermes. Most of the rules arede�ned in machine-readable tables. We explain how to read the tables.We explain the other rules informally but rigorously. The manual containsexamples of how to apply the rules, but these examples are not necessarilytypical or preferred uses of Hermes.We assume that you have already read and understood the Hermes tu-torial. We also assume that you have experience with some other program-ming language. Therefore, we will not explain basic concepts such as storedprograms, compilation, BNF, etc.Here are the stages of writing and executing a Hermes program.� You write the code using your favorite editor/program generator,producing a source �le.� The �rst stage of the compiler converts your source �le into a Hermesprogram value.� The Hermes compiler checks the Hermes program, producing a checkedprogram value. The internal representation of a checked Hermes pro-gram will include its translation to machine code. Like all internalrepresentations, this will be invisible to the programmer.� You store the checked program into a program library.� You retrieve the checked program, instantiate it as a process, andexecute it.You may also generate a Hermes program value directly within a Hermesprogram. This bypasses the �rst two steps mentioned above.In this manual, we ignore the �rst step because we do not care how youproduce the source �le. The remaining steps are broken down as follows:� Lexical analysis: The characters in the program are divided into to-kens. Space and comment tokens are thrown away, and the othertokens are passed as input to syntax analysis. The lexical rules arewritten in a formal grammar. Lexical analysis is explained in chapter7.� Syntax analysis: The stream of tokens is parsed, revealing the struc-ture of the program.The syntax rules are written in a formal grammarin Appendix A.

72 6. Introduction� Conversion and resolution: The parsed program is converted intoa Hermes program value. This requires the compiler to resolve allnames. Names are made up of identi�ers. For each identi�er there isa de�ning occurrence where the identi�er is associated with its de�-nition. Other appearances of of the identi�er are called applied occur-rences. Resolution means associating each applied occurrence with ade�ning occurrence. The resolution rules are described informally inchapter 8.� Type checking and inference: The type of each variable must satisfythe type rules for the operation where the variable appears as anoperand. For variables without declarations (expression temporaries),the compiler applies type inference to generate a declaration. It ischecked that exactly one type can be inferred and that this typesatis�es the type rules. The type rules are given in an appendix. Thetype checking and inference algorithm is explained in chapter 9� Typestate checking: The typestate at each point in the program iscomputed. It is checked that the typestate at each operation is con-sistent with the typestate requirements of that operation. Coercionsare generated where necessary. The typestate rules are given in anappendix. The typestate checking algorithm is explained in chapter10.� Other static restrictions: These are constraints on the value of a pro-gram other than those captured in the table-driven rules. Example:The number of arguments to call must match the number of com-ponents in the callmessage type. These restrictions are described in-formally next to the constructs they restrict.� Execution: We describe the semantics of each operation rigorouslybut informally in chapter 11.

7Lexical and Syntactic RulesLexical analysis separates the source program into tokens. Tokens are col-lections of characters that constitute the elementary syntactic units. Thedi�erent kinds of token in Hermes are: spaces, comments, integer literals,real literals, named literals, string literals, punctuation, and words.Spaces are used to separate tokens. Comments are used for human doc-umentation, but to the computer they are equivalent to spaces. There aretwo commenting styles. A double dash \--" starts a comment that runsto the end of the line. Comments within a line begin with \/�" and endwith \�/". Comments delimited with \/�" and \�/" may not extend acrossmultiple lines. Because of this rule, you need not look at a context biggerthan a line to see what is a comment and what is not. To \comment out"a set of statement lines, precede each line with a double dash.A literal token de�nes a compile-time known value.� An integer literal token is a decimal representation of an unsignedinteger. Some examples of integer literals are 0, 112358, and 000.Notice that -2 is not a token. It is parsed as a unary operator appliedto an integer literal.� A real literal consists of an unsigned integer part, a decimal point, afraction part, an \e" (upper or lower case), and an optionally signedinteger exponent. The integer part must have at least one digit, butthe fraction part can be empty. The symbol \e" means \times tenraised to the power of". Some examples are 15., 0., 1.12358,0.314159e+02 and 314.159e-02.� A named literal is represented by a nonempty string of charactersenclosed in two single quotes such that, inside the delimiting quotes,single quotes always occur in pairs. The value of a named literal isthe string of characters inside the delimiting quotes where every pairof consecutive single quotes is regarded as one single quote. The caseof the characters is signi�cant in named literals. Some examples ofnamed literals are 'true' and 'Isn''t'. A named literal cannotextend past a single line.� A string literal is represented by a (possibly empty) string of char-acters enclosed in two double quotes such that, inside the delimitingquotes, double quotes always occur in pairs. The value of a stringliteral is the string of characters inside the delimiting quotes whereevery pair of consecutive double quotes is regarded as one double

74 7. Lexical and Syntactic Rulesquote. The case of the characters is also signi�cant in string literals.Some examples of string literals are "" (the empty string), "quothhe", and "A "" symbol begins a quotation". A string literal can-not extend past a single line.Punctuation consist of one or two characters. They are used for some oper-ator names and for grouping and delimiting syntactic units. Some examplesare parentheses { ( and ) { and the move operator: <-. The legal punctu-ation tokens are presented in Table A.2 in Appendix A.A word is a string of characters that are alphanumeric characters orthe underscore character. A word cannot begin with a numeric character.Words are used either identi�ers (programmer-de�ned names) or keywords(words serving as syntactic markers). Some keywords cannot be used asidenti�ers. They are called reserved words. The case of the charactersof a word token is ignored. Some examples of identi�ers are AbCd 123,foo, and x. The keywords and reserved words are listed in Table A.1 inAppendix A.Hermes's syntax is given in Appendix A. It is written in BNF, using theyacc ([ASU88, AU77, KP84]) notation.

8ResolutionResolution means matching applied occurrences of names with their de�n-ing occurrences, and rejecting unde�ned applied occurrences or con ictingde�ning occurrences.The following classes of names appear in Hermes: variable name, typename, attribute name, exception name, exit name, and process name. Namesconsist of one or more identi�ers. Each identi�er in an applied occurrenceof a name must be resolved.For each class of name, there are rules specifying:� the name space in which the name is de�ned. A name space is an as-sociation between identi�ers and their de�nitions. Within each namespace there can be only one de�ning instance of a particular identi-�er. (Remember that case is ignored, so x and X cannot appear in thesame name space.)� the regions where each name space is visible | that is, where theidenti�ers can be referred to by applied occurrences.� how to disambiguate a name if the name being resolved is in morethan one visible name space. It is a resolution error either if thereis no visible de�nition, or if there is more than one visible de�nitionand no disambiguation is possible.A name is said to be builtin if its meaning is given in the Hermes languagede�nition and there is no de�ning occurrence. For example, the typestateattribute init and the exception name NotFound are builtin. A name issaid to be prede�ned if it is de�ned in the predefined module, which isimplicitly imported by all Hermes modules. For example, the type namesCharstring, integer and program are prede�ned. Programmers do nothave to supply de�nitions for either builtin or prede�ned names.8.1 Variable NamesA variable name denotes a container for a value. Variable names are usedas operands.variable{name::= base{variable [ . component{name ] . . .

76 8.1. Variable Namesbase{variable::= identi�erExamples: Foo, Foo.Bar, Foo.Bar.Baz.8.1.1 Base VariablesBase variables are de�ned within name spaces called declarations lists. Theregion of visibility of a declarations list is called its scope. Scopes ap-pear only within executable code | either process modules or constraintde�nitions. Scopes can be nested. In an executable module, the processbody is the outermost scope. The declarations list comprises the the ini-tialization port declaration and the base variables declared following thedeclare keyword. The block statement de�nes an inner scope, in whichthe programmer can declare base variables. The selector, the inspect, thefor statement, and the expression block also introduce scopes containinga single base variable declaration. Scopes also appear within constraintde�nitions. The outermost scope in this case is the declaration list of theconstraint de�nition.It is illegal for the same identi�er to be de�ned within two overlappingscopes. (In other languages, this inner declaration would \hide" the outerdeclaration producing a \hole" in the outer variable's scope.) It is permis-sible for the same identi�er to be declared twice within a program if thescopes do not overlap. Example:blockdeclareX: integer;T: table of integers;beginfor X in T [] -- illegal redeclaration of Xinspectcall PutLine(IntToString(X));end for;end block;for T in Z where(T > 0) -- legal redeclaration of Tinspectcall PutLine(IntToString(T));end for;The resolution rule for base variables is trivial: A base variable is visiblewithin its scope. Because base variables cannot have overlapping scopes,there can be a maximum of one visible de�ning occurrence of any basevariable.

8. Resolution 778.1.2 Component NamesComponent names are de�ned in record, variant, and callmessage typede�nitions. Each record, variant, or callmessage type de�nition de�nes aname space for component names. This implies that a record type R cannothave two di�erent components named X, but two di�erent record types caneach have a component named X.Variable names containing components are resolved from left to right.First the base variable is resolved. Then the component of a variable isresolved within the component name space associated with the variable'stype de�nition. For example, in the name X.Y.Z, I �rst resolve the name X.Then I look up X's type. I look up that type's de�nition to �nd the namespace within which I can resolve Y. I can then look up Y's type, and repeatthe process to obtain the resolution of Z. Note: a type may be known evenwhen the type name itself is not visible | e.g. in the above case the typeof X.Y might not be visible, because the de�nitions module did not appearin the using list.8.2 Type NamesType names are used in variable declarations, in type speci�ers, and in def-initions. Type names are written either simple identi�ers (type identi�ers),or they are type identi�ers preceded by a de�nitions module name.type{name::= de�nition{name::= module{name ! de�nition{namede�nition{name::= identi�erType identi�ers are de�ned within a de�nitions module|either a de�ni-tions module written by the user, or the predefined module. The set oftype de�nitions within a de�nitions module forms a name space|that is,two type de�nitions within the same de�nitions module must have distinctnames.Within a de�nitions module, the following type de�nitions are visible: (1)those of the de�nitions module itself, (2) those in predefined, (3) those inde�nitions appearing on the imports list.The imports list appears at the header of every module. It lists the namesof de�nitions modules whose de�nitions will be made visible.Within an executable module, there are no type de�nitions, so only typede�nitions of categories (2) and (3) are visible.A type name not preceded by a module name is legal if and only if thereis exactly one visible de�nition of that type.

78 8.2. Type NamesIf a module name is speci�ed, the type name refers to the de�nitionwithin the speci�ed module. The module name must be either predefined,or a de�nitions module on the imports list.8.3 Attribute NamesAttribute names are used in typestates. The syntax of attribute names isidentical to the syntax of type names.The names init and case are builtin typestate attributes. The namefull is a builtin abbreviation for a set of init attributes. The namechecked denotes a builtin constraint attribute. The name checkeddefinitionsdenotes a builtin attribute described in section 11.10. All other attributenames are names of constraints de�ned in de�nitions modules. The set ofconstraint de�nitions of each de�nitions module forms a name space. ofconstraint attributes That name space is di�erent from the space of typede�nitions, so it is legal to de�ne a type T and a constraint T in the samemodule.Except for the special treatment of the �ve builtin names, the resolutionrules for attribute names are identical to the resolution rules for type names.8.4 Exception NamesException names are used in exception handlers, and on the return state-ment. Exception names are either builtin, or user-de�ned. The two havedi�erent syntax.Builtin exception names denote the exceptions associated with Hermesprimitive operations. A builtin exception name is written as a simple iden-ti�er. The identi�er must be one of the names in the prede�ned typepredefined!builtin exception.User-de�ned exception names denote the exceptions returned from callstatements. They are written as:exception{name::= type{name . user{exception{nameOn the return statement, the type name is understood to be the sameas the type of the callmessage operand, so only the exception identi�er iswritten. There is a name space of exception identi�ers for each callmessagede�nition. That name space contains the user-de�ned exceptions, plus theautomatically de�ned exception discarded. A user-de�ned exception nameis resolved simply by resolving the type name. The type name must be acallmessage type. This determines a single name space which is used toresolve the exception name.

8. Resolution 798.5 Exit NamesThe exit statement allows the programmer to jump to a handler clausewithout raising a builtin exception or issuing a call. Exit names are de�nedon the handler and used on the exit statement.The de�ning occurrence of an exit name is a handler beginning on exit.The name space is the identi�er list appearing after the word exit. Thename is visible throughout the main clause of the block containing thehandler. Unlike base variables, there can be several overlapping regions ofvisibility for the same exit name.The applied occurrence of an exit name is on an exit statement. Whenresolving an exit name, if there are several visible de�nitions of the sameexit name, the innermost visible de�nition is used.8.6 Process NamesA process name is used only in the program literal construct:program literal : process identifierThe identi�er must be present in the linking list of the source module.The identi�er denotes the process module of that name.

9Type Checking and InferenceAfter a module has been tokenized, parsed, and resolved, it is stored as aHermes program value. This is a much simpler form as far as the computeris concerned. All the \noise words", like of, from and into, which make theprogram easier for people to read but less syntactically regular, are gone.Statements with embedded expressions are expanded out to sequences ofstatements. Names, like X, whose resolution may be context-sensitive, havebeen removed. In their place are unique identi�ers.The Hermes program value is: (1) a collection of type de�nitions andconstraint de�nitions, organized into de�nitions modules; (2) a collectionof declarations, organized into \scopes"; (3) a collection of clauses. Eachclause is a series of statements, and each statement is an operator, a list ofoperands (variable names), and a \quali�er", which is everything else. Bythis time, all base variable names except expression temporaries have beendeclared.The job of the next phase is: (1) type inference: to supply declarations forthe expression temporaries so that every base variable will have a knowntype; (2) type checking: to check that the operations are legal, given thetypes of the operands.Both type inference and type checking are driven from a single table ofrules. There are two families of rules: inference rules, and class rules. Theinference rules are used for both inference and checking. The class rules arean extra set of checks which apply after type inference is complete.The rules appear in the appendix. The syntax of an inference rule is:inference rule : operand rule name ( )inference rule : operand rule name ( operand )The �rst kind of rule unconditionally assigns a type to the operand onthe left. The second kind of rule is �red when the type of the operand onthe right becomes known. The rule provides a function which determinesthe type of the operand on the left.The type rules in the table entry for this operation look like this:insert at( table, element, position) absenttable 2 orderedtableposition predefinedinteger()element elementtypeof( table)For example, let's look at the operation insert at. The header line statesthat the operation insert at has three operands, which will be referredto in the rules as table, element, and position. There is no quali�er. The

9. Type Checking and Inference 81header line is followed by three type rules. The �rst rule is a class rule, whichstates that the type of table must be an ordered table type. The second rulesays that the operand position|this is the operand after the word at|isunconditionally inferred or checked to be of type predefined!integer.The third rule says that if the type of the table is known, then the type ofthe element will also be known by applying the function elementtypeof.The type checking algorithm begins by computing all known types|thetypes of those operands which are variable names, and of those expressionresults which were explicitly marked with a type speci�er. For each opera-tion, the inference rules with empty right-hand sides, or whose right-handside operand is known are �red. Each time an inference rule is �red, itinfers the type of the operand on its left side. If that operand currently hasknown type, the inferred type is checked against the known type. A mis-match is an error. If the operand currently has unknown type, the inferredtype is assigned by generating an inferred declaration for the operand. Nowthat the type of this operand is known, this may cause other rules to �re.Type checking continues until no additional �rings are possible. By now,all variables should have been assigned types. If any variables still haveunknown types, the program is in error and the compiler will ag eachoccurrence of such variables.There are some variables which, for technical reasons, are not consid-ered operands, but are instead grouped with the quali�er. Type inferenceand type checking are also performed for these variables, but those checksare not table-driven. They are noted in the appendix as comments. Forexample, the test variables used in if, while, select, and the predicatein a selector (the expression following the keyword where) are inferred aspredefined!boolean. Because of this inference, it is legal to write ex-pressions like if (a > b), which otherwise would be ambiguous since theoperation > can return a result of any boolean type. This inference canlead to detection of type errors elsewhere, e.g. if (3) ... will produce amessage that the integer-literal operation cannot yield a result of typepredefined!boolean.After all types have been inferred, the class rules are invoked. A class ruleis a constraint on the possible types a given operand can have. The class ruleis applied regardless of whether the operand's type was inferred or originallyknown. For example, the table operand of the insert at operation mustbe an ordered table. There is no class rule for the element operand, sinceit may be of any type. Since an inference rule already guarantees that theposition operand is of type predefined!integer, a class rule would beredundant.Note: Type checking and inferencing is performed after resolution. Typecorrectness is never used as a criterion for disambiguating a name.Here are four examples illustrating type inference and checking. Assumethat type Charstring is a table of Char, and that Queue is a table ofCharstring.

82 9. Type Checking and InferenceblockdeclareX: Charstring;Q: Queue;I: Integer;beginnew Q;insert "abc"|"cd" into Q; -- inference from top downX := "uvw";I := size of (X | "abc"); -- inference from bottom upinsert X | "abc" into Q; -- inference in both directionsI := size of "abc"; -- no inference; illegalend block;The relevant operations are: (1) string literal|the class rule says thetype must be a string, but there is no inference rule; (2) size of|there isan inference rule for the result type (prede�ned integer), but none for thesource type; (3) insert|the type of the element can be inferred from thetype of the table, (4) concatenation|there are two sources and a result, allof the same type, so if any one type is known the other two can be inferred.In the �rst statement, the type of "abc"|"cd" is inferred to be Charstringbecause the type of Q is known to be Queue. The type of "abc" and "de"are inferred to be Charstring because the rules for concatenation can nowbe �red.In the second statement, the type of X | "abc" cannot be inferred fromsize of, because even though the result type is known to be integer, thereis no inference rule to infer the source type. But since the type of X is knownto be Charstring, the rule for concatenation determines that the types of"abc" and of the result, X | "abc" must be Charstring, too.In the third statement, the inference can work in both directions. Becausethe type of Q is known, the rule for insert can �re to determine the type ofX | "abc". Because the type of X is known, the rule for concatenate can �reto determine the type of X | "abc". Whichever rule �res �rst will set thetype; whichever rule �res second will check the type. If the types inferredin the two directions had been di�erent, an error would have resulted.In the fourth statement, no type inference is possible. The string literaloperation doesn't infer the type|it only has a class rule. (Hermes has noAda-like rule which tries to search the space of all visible types to seewhether only one legal type is visible.) The size of operation likewise hasno inference rule for the source operand. Since there is no type assignmentto "abc", the statement is illegal|it needs to be rewritten I := size ofCharstring # "abc".The pre�x charstring # is called a type speci�er. The e�ect of a typespeci�er is to explicitly declare a type for an expression operand. When atype speci�er is used, the temporary variable is treated as having knowntype rather than unknown type. This known type still must be consistent

9. Type Checking and Inference 83with the inference rules and class rules. The example above shows thatsometimes a type speci�er is required. Type speci�ers may also be used fordocumentation purposes even where they are not required.

10Typestate CheckingTypestate checking assigns a typestate to each program point. A typestateis a mathematical object which can be interpreted in two ways: (1) as anassertion that certain properties of program variables will be true when-ever that statement is executed; (2) as a point in a semilattice 1. Becausetypestates are assertions, they can be used to check at compile-time thatcertain language rules expressed in terms of those assertions are satis�ed.Because typestates form a semilattice, certain well-known algorithms fromdata ow analysis can be used.In this section, we will discuss the particular typestate semilattice usedby Hermes. We will describe the typestate checking algorithm we use. Wewill tell you how to read the table which de�nes the particular typestaterules for each operation.10.1 Syntax of TypestatesTypestates are explicitly written in the following situations: (1) in inter-face de�nitions (input ports, callmessages, and constraints), (2) in table andvariant de�nitions to indicate what typestate an element or variant com-ponent must have, (3) in the unwrap statement, to indicate what typestatea polymorph must have.A typestate is written as a set of attributes separated by commas andenclosed in curly braces. An attribute is an attribute name followed by alist of variable names called attribute arguments.typestate ::= f [ attribute [ , attribute ] . . . ] gattribute ::= attribute{name attribute{argumentsExamples of attributes: init(X.A), case(V, V.A), greater than(X,Y).The following attribute names are built-in: init, case, checked andcheckeddefinitions. Constraint attributes, like greater than, are user-de�ned.The arguments of builtin attributes are checked as follows: Attributeinit must have a single argument, and case must have exactly two argu-1A semilattice is a set with a partial order relationship � such that any pairof elements t1 and t2 have a unique greatest lower bound or meet t such thatt � t1; t � t2 and for any t0, (t0 � t1 ^ t0 � t2)) (t0 � t).

10. Typestate Checking 85ments. The �rst argument to case must be a variant variable, and the sec-ond must be a component of the same variant variable. Attribute checkedmust have a single argument of type predefined ! program. Attributecheckeddefinitions must have a single argument of type predefined !definitions module.All other attribute names are constraint attributes. When you mentiona constraint attribute, there will be a constraint de�nition which speci�eshow many attribute arguments there must be and what the type of eachmust be.For example, if there is a constraint de�nitiongreater than: constraint (a: integer, b: integer) ISfinit(a), init(b)g a > b;then the attribute greater than must always appear with exactly twoarguments of type integer.The name full is syntactically an attribute name, but it is treated as anabbreviation. If you write full(X), and X is a record or callmessage, thenit's the same as if you wrote init(X) and full(X.A) for each componentX.A of X. For other types, full means the same as init. From now on,we'll assume full has been expanded out wherever it appears. Because ofthis use as an abbreviation, you may not de�ne an attribute named full.Here are the meanings of the typestate attributes:� init(X) means the variable X is initialized; otherwise it is uninitial-ized.� case(V, V.X) means that variant V is revealed, and component V.Xexists; the absence of case(V, ...) means that variant V is hidden.� checked(P) means that program P has been checked and is free ofsyntax errors, including resolution errors, type errors, and typestateerrors.� checkeddefinition(D) means that de�nitions module D has beenchecked and assigned a unique name. (See the discussion of the checkdefinitionsstatement.)� attributename(arguments)means that the named constraint pred-icate is known to be true of its arguments.10.2 Formal TypestatesSometimes a typestate must be written without the base variables. Forexample, here is the de�nition of a record, and a table of these records:R: record(A: integer, B: Charstring);

86 10.2. Formal TypestatesT: table of R finit(*), init(A), init(B)g keys(A);The de�nition says that the typestate of an arbitrary variable V of type Rbefore being inserted into a table of type Tmust be finit(V), init(V.A),init(V.B)g. When we write the de�nition, we omit the arbitrary variableV. The resulting typestate is called a formal typestate. A formal typestatelooks like a typestate, except that we omit the base variable from eachargument of each attribute, turning it from a variable name into a formalvariable name. If the variable name is a base variable with no components,it is replaced by *. The argument list (*) can optionally be omitted.Formal variable names are also used in key lists, where they denote com-ponents of an arbitrary element of a table.A formal variable name or formal typestate is always written in a contextin which the type of the arbitrary variable is known.To substitute a variable name into a formal typestate means to pre�xeach formal variable name with the given variable name.For example, if variable name X.Y has type R, then you can substitute X.Yinto the formal typestate in the de�nition above, and obtain the typestatefinit(X.Y), init(X.Y.A), init(X.Y.B)g. You can substitute X.Y intothe key and obtain an actual key of X.Y.A.10.3 Valid TypestatesBecause of the meaning of the Hermes data types and the meaning of theattributes, not every combination of attributes is valid.Here are the attribute compatibility rules which de�ne certain combina-tions as invalid:� If init(X) is not present, no other attribute with argument X maybe present. This follows from the fact that only init variables canhave values.� If X has components, and init(X) is not present, then no other at-tribute with any component as argument may be present. This followsfrom the fact that components of structured values only exist whenthe structured value itself exists.� If case(V, V.A) is present, then no other attribute case(V, V.B)is present. This follows from the fact that only one component of avariant exists at a time.� If case(V, V.A) is absent, then no other attribute with V.A as com-ponent may be present. This follows from the fact that you can accesscomponents of a variant only when they are revealed.� If case(V, V.A) is present, then V.A's case typestate attributes arepresent. These are the attributes obtained by substituting V.A into

10. Typestate Checking 87the formal typestate de�ned for component A in the type de�nition forvariant V. This follows from the fact that we require these attributesto be present when we hide the variant, and we require that thetypestate of a revealed variant must be higher than the typestate ofan initialized but hidden variant.� If init(CM) is present and CM is a callmessage, then the attributesin the minimum typestate of CM must be present. These attributesare obtained by substituting CM into the formal typestate de�ned orimplied as the minimum typestate. (See discussion of callmessages.)This is because the programmer is allowed to state in the interfacethat certain call parameters must remain initialized and cannot bediscarded by the process receiving the call.Otherwise legal operations whose e�ect would be to produce an invalidtypestate are illegal|e.g. an assignment to record component R.A when Ris itself uninitialized.10.4 Ordering of TypestatesThe semilattice structure is simple. A typestate is lower than a secondtypestate when the set of attributes of the �rst is a subset of the set of at-tributes of the second. The meet of two typestates is simply the intersectionof the attribute sets.Example: finit(A)g is lower than finit(A), even(A)g. The meet offinit(V), case(V, V.A), init(V.A)g and finit(V), case(V, V.B),init(V.B)g is finit(V)g.10.5 CoercionsCoercions are typestate-lowering operations which are inserted automati-cally by the compiler. There are three coercions:� discard X removes the attribute init(X), as well as any other at-tributes of X or X's components.� hide V removes the attribute case(V, V.X), along with any otherattributes of V.X.� drop constraint(P1, ..., Pn) removes the attribute constraint(P1,..., Pn).

88 10.5. Coercions10.6 ConstantsAlthough we use the term \variable" to denote a cell which can hold a valueand appear as an operand, sometimes \variables" are \constant"! That is,sometimes it is only legal to access the value of a variable but not to changeit.A variable is constant at a particular program point if:� It is a component of an initialized callmessage, and the callmessagetype de�nition lists that component as a constant.� It is a base variable, and an enclosing block statement lists that basevariable as constant.� It is a base variable which was initialized to a constant copy. (Seeselector, inspect, for statements.)� The program point is inside an expression block and the base variablewas declared outside that expression block.� It is a component of a constant.10.7 How to Apply the Typestate RulesHere are the steps in checking a single statement and propagating a newtypestate to its successor statements:1. Determine the precondition for the operation. There is a table givinga set of precondition rules for each operation. These rules determine:(1) a set of required attributes, (2) a set of forbidden attributes,(3) the operands modi�ed by the operation, (4) possible additionalexceptions.2. If any required attribute is missing, this is an error.3. Introduce coercions which drop attributes which are present but areforbidden. If we cannot add coercions without also dropping requiredattributes, then this is an error. Otherwise, we have succeeded ingenerating a typestate which is consistent with the preconditions.4. Generate an error if any variables modi�ed by the operation are con-stants.5. Determine the set of outcomes of the operation. There is a \nor-mal" outcome for all operations (except exit), and there can be sev-eral \exception" outcomes. The table entry for each operation de�neswhich exception outcomes the operation has. The table may list addi-tional exceptions which the operation has under certain circumstances

10. Typestate Checking 89| for example, it may say that the operation has a DuplicateKeyexception outcome provided its operand is a keyed table.6. Determine the postconditions for each possible outcome of the oper-ation. For each outcome, use the rules given in the table to determinewhich attributes to add to or drop from the typestate.7. Check that after adding and dropping the appropriate attributes theresulting typestate is valid.8. When each typestate is computed, propagate that typestate to thedestination. If that destination already has a typestate, compute themeet (as de�ned above) of its previous typestate and the typestateyou have just computed for this path to that destination. Insert co-ercion operations to lower the typestate to that meet typestate.Let's apply these steps to an example. Suppose I have these type de�ni-tions:NamedProgram: record(A: Charstring, B: Program);ProgramRepository: table of R ffullg keys(A);Now suppose I is of type integer, P is of type NamedProgram and R isof type ProgramRepository. Suppose the current typestate is: finit(I),init(R), init(P), init(P.A), init(P.B), checked(P.B)g. Suppose thestatement being checked is:insert P into R;Look at the table of operation rules in Appendix B. After the class rules,there are three rules relevant to typestate checking. These are: the precon-dition rules, the postcondition rules, and the exception list. For insert,the precondition rules are(init(table), lowestelementstate(element, table), var(table),var(element), duplicatekey?(table))The �rst two members of the list are typestate precondition functions.Rule init(table) says that the table operand must be initialized. Rulelowestelementstate(element, table) says that the element operand mustbe in the lowest typestate consistent with the element typestate of the table.(Element typestate is de�ned below in section 11.6.) The element typestateis ffullg, which is an abbreviation of finit(*), init(A), init(B)g.Substituting P into this formal typestate yields finit(P), init(P.A),init(P.B)g. Any attributes of P or of its components other than the aboveattributes are forbidden. Section B.4 explains how to apply each precondi-tion function to determine the required and forbidden attributes.As a result of applying the precondition rules, it is determined that theseattributes are required: finit(R), init(P), init(P.A), init(P.B)g.Thisattribute is forbidden: checked(P.B). Since all the required attributes

90 10.7. How to Apply the Typestate Rulesare present in the typestate, there is no \missing attribute" error. How-ever, there is an \extra" attribute|checked(P.B). The coercion dropchecked(P.B) is inserted to drop the checked(P.B) attribute. This causesthe compiler to forget that the program value P.B has been checked, be-cause it is being inserted into a table containing program values whichare initialized but not checked. When the value is removed from the ta-ble, it will be assumed to be unchecked. The typestate is now finit(I),init(R), init(P), init(P.A), init(P.B)g.The rules var(table) and var(element) tells us which operands are mod-i�ed by the operation. From this rule, we infer that P and R must not beconstant. If they are, the compiler ags an error.The \rule" duplicatekey?(table) is not a precondition rule. For techni-cal reasons, it is included with the precondition rules and evaluated whenpreconditions are evaluated. The e�ect of the rule is to check whether thetable R was de�ned as keyed, and if so, to include DuplicateKey in the setof exceptions which could be raised.The possible outcomes of insert are: normal, Depletion, and DuplicateKey.The Depletion exception was determined from the exception list, becauseevery insert operation can raise this exception. The DuplicateKey wasderived by evaluating the predicate duplicatekey?(table), because onlyinsertions into keyed tables can raise this exception.The postcondition for normal exit is determined by the postconditionrules. For insert, these rules are:(makeuninit(element), killconstraints(table))Except for the call statement, all exception outcomes produce an un-changed value and an unchanged typestate|as if the operation had notexecuted. So the typestate associated with the two exception outcomesis also finit(I), init(R), init(P), init(P.A), init(P.B)g. For thisreason, exception postconditions are not shown in the table.The rule makeuninit(element)makes P uninitialized by dropping all at-tributes of P and its components|namely finit(P), init(P.A), init(P.B)g.The rule killconstraints(table) drops any constraint attributes of thetable|there were none. The postcondition functions are explained in sec-tion B.5.So the typestate associated with normal completion of insert is: finit(I),init(R)g.10.8 The Checking AlgorithmThe initial typestate is one in which the initialization port is initialized andno other typestate attributes are present.At each statement, the rules in the table are used to determine the re-quired and forbidden attributes prior to the operation, and the added and

10. Typestate Checking 91dropped attributes after normal completion operation.Three types of statements are handled specially:� The call statement is di�erent because the normal and exceptionpostconditions come from the interface de�nition.� The compound statements have more complex postcondition rulesbecause they are equivalent to miniature ow graphs. For instance,an if statement behaves as if it were composed of a more primitiveboolean test statement and the embedded clauses. In the referencemanual we describe the typestate postcondition resulting from this ow graph.� Similarly, every statement containing a selector is analyzed as if itwere two statements: a selector followed by a primitive statement.The rules in the table de�ne the precondition and postcondition rulesfor the primitive statement; these rules are applied after the rules forselectors.Exceptions, exit statements, and the branches from the end of clauseswithin block, if and select statements to the end of the statement areforward branches. When a forward branch is encountered, the meet is takenbetween the previous typestate associated with the destination and thetypestate propagated from the statement performing the forward branch.Each loop construct (while, for) performs a single backward branch fromthe end of the loop to the beginning. If the typestate at the end of the loopis strictly greater than or equal to the typestate at the beginning, nothingis done except to introduce any necessary coercion. If the typestate atthe end of the loop is lower than or incomparable to the typestate at thebeginning, the meet is taken between the two typestates. This lowers thetypestate previously associated with the top of the loop. The statementsof the loop are then re-analyzed.Analysis continues until all program points have been assigned a types-tate and no loops need to be re-analyzed. In practice, no loop will need tobe analyzed more than twice.10.9 Typestate ErrorsThe possible typestate error messages are:� Dead code: A statement cannot be reached. Remember that types-tate checking ignores the values of variables, so that even a statementbeginning if 'false' then ... does not generate a dead code ex-ception. This error arises when you write a statement after an exitstatement or after a compound statement all of whose alternatives

92 10.9. Typestate Errorsterminate with exit statements. It also arises if you code a handlerwhich can never be branched to.� Attribute not present: A required typestate attribute was not present.For example, you coded an insert operation but the table was notinitialized.� Cannot coerce: The operation requires an attribute to be dropped,but the coercion which drops the attribute would also drop anotherattribute which is required. Example:call Service(Parms.A, Parms.B);Suppose the interface to Service requires the �rst argument to beinitialized and the second to be uninitialized. Suppose Parms is acallmessage whose minimum typestate requires both Parms.A andParms.B to be initialized. Then the call statement requires Parms.Bto be made uninitialized, but this cannot be done without discardingParms, which would then make Parms.A uninitialized.� Cannot add: The postcondition rule mandates adding this attribute,but to do so would produce an invalid typestate. For example, anassignment to Parms.Awithout �rst initializing Parms would producethis message.� Cannot drop: The postcondition rule mandates dropping this at-tribute, but to do so would produce an invalid typestate. For example,dropping a callmessage below its minimum typestate.The following, while not technically typestate errors, are static context-dependent errors which are detected during typestate checking:� Illegal constant: You have coded a statement which modi�es one ofits operands, but the operand is constant.� Illegal position: You have coded the position of operation in acontext where it is not legal. This operator is legal only when thetable element operand is a constant copy.

11Hermes OperationsThroughout the remainder of the reference manual, syntactic rules will beexcerpted from Appendix A as speci�c language constructs are introduced.Low-level syntactic elements that appear in rule bodies generally will not beincluded in the excerpts. The grammar is presented in its entirety, includingthese low-level elements, in Appendix A.11.1 Ubiquitous OperationsUbiquitous operations are those which apply to all or nearly all types.Movesimple{statement::= result{variable source{expressionThe move statement removes a value from one variable 1 (the source) andplaces it in another (the destination). If the destination variable had avalue, that value is discarded. The source variable becomes uninitialized.Example:R.A <- X;The source and destination variables must have the same type. Hermesuses \name-equivalence" for deciding whether two types match; therefore,types with identical domains or identical structures don't match.Values of any type or typestate except totally uninitialized can be moved.However, neither source nor destination can be constant. Any constraints(e.g. greater than(X, Y)) of the source become constraints of the destina-tion (e.g. greater than(R.A, Y)). Any previous constraints on the targetR.A or on a containing variable R are dropped.Throughout the manual, if we say \a value is moved" this means thatthe value no longer exists in its old place. This is distinct from saying \avalue is copied", which means that the value remains in its old place, buta copy also exists in the new place.1Here and elsewhere, the term \variable" denotes any operand. Some \vari-ables" are unnamed temporary variables holding the results of expressions. Some\variables" are constants.

94 11.1. Ubiquitous OperationsCopysimple{statement::= result{variable := source{expressionsecondary::= copy of secondaryThe copy of statement copies a value from one variable to another. Thesource variable retains its original value and typestate attributes. As withmove, the destination variable loses its old value and attributes, but acquiresthe value and attributes of the source variable. The source variable mayhave any typestate except completely uninitialized.There are two ways of writing a copy statement: as an expression likecopy of X, or in an assignment statement like Y := X. The latter is equiv-alent to Y <- copy of X.As with the move statement, source and destination must have identicaltype.Input ports and callmessages are uncopyable, as are aggregates contain-ing uncopyable data, or polymorphs containing wrapped uncopyable val-ues. If it is known from the type that a value is uncopyable, the copyoperation will be rejected at compile-time. If it cannot be statically de-termined whether the variable is copyable (because the value includes apolymorph or variant), then the copy operation will be legal, but will raisethe Uncopyable exception at execution time if the value is actually un-copyable.Other operations besides copy of have the e�ect of copying values orparts of values. For example, every of produces a copy of a subset of atable. As with the copy operation, if the value's type is known staticallyto be uncopyable, then the statement is a compile-time error. If the type isnot known statically to either be copyable or uncopyable, the Uncopyableexception will be raised at execution time if the value is in fact uncopyable.Copies, or transitive copies of copies are always equal. Depending on thetype, other values besides copies may be equal.Equal, Not-equalrelation ::= concat = concat::= concat <> concatValues of any type, and of any typestate except totally uninitialized, canbe tested for equality with the operators = and <>. For each type family,equality will be de�ned.Operands being compared for equality must have the same type.

11. Hermes Operations 95Discardsimple{statement::= discard variable{nameThe discard statement removes the value from the variable. Discarding aninitialized callmessage returns the callmessage; the caller will receive thecallmessagetype.Discarded exception. The callmessage components will�rst be lowered to their minimum typestate|this concept is de�ned later,in the section on callmessages. Discarding a value containing callmessages|for example an input port holding enqueued callmessages|discards thecallmessages. Discarding any other type of value just throws the value away.11.2 The Depletion ExceptionMost operations can raise this exception, so it is described here rather thanrepeatedly under each operation.An ideal Hermes machine has unlimited resources. Real machines havelimited resources. The Depletion exception is raised whenever due to a re-source limitation or an implementation restriction, the correct result cannotbe computed.Examples include: running out of address space, exceeding disk capac-ity limits, integer over ow. Implementations are encouraged to avoid giv-ing Depletion exceptions rather than to require programmers to developtheir own circumventions. For example, integers which don't �t in a hard-ware register could be represented by dynamic-precision integers. But wedon't require all implementations to be this sophisticated, so we reservethe Depletion exception to inform the user that the implementation isinadequate to run this particular execution.Coercion operations|e.g. discard| cannot fail with any exception.Hermes implementations must guarantee that storage depletion cannot oc-cur while discarding or hiding a value.

96 11.2. The Depletion Exception11.3 Control Flow OperationsBlockcompound{statement::= block[ constant{section ][ declaration{section ]begin[ statement ; ] . . .[ handler ] . . .end blockconstant{section::= constant ( [ base{variable [ , base{variable ] . . . ] )declaration{section::= declare [ declaration ; ] . . .handler ::= on ( [ exception{name [ , exception{name ] . . . ] )[ statement ; ] . . .::= on exit ( [ exit{name [ , exit{name ] . . . ] )[ statement ; ] . . .A block statement introduces a new scope. Within this scope, you candeclare new variables, you can specify that the values of existing variablesmust remain constant, and you can provide handlers for exceptions or exitswithin the main body of the block.The statement series after the word begin is themain clause of the block.The other clauses are the handler clauses. Each handler clause is precededby the names of the exits or exceptions it handles. The abbreviation othersstands for all exceptions which are not explicitly handled. Restriction:Two clauses of the same block cannot handle the same exit or exception.An exception or exit raised within the main clause of a block causescontrol to transfer to the clause headed by that exception or exit name.If a statement is within the main clauses of two or more nested blockscontaining handlers for the same exception or exit, control will transferto the handler of the innermost block. Remember that exceptions raisedwithin a handler clause cannot cause control to transfer to another handlerclause of the same block.If all statements of a clause complete without raising an exception orissuing an exit statement, then the clause is said to exit normally. . If anyclause of a block exits normally, then the block itself exits normally.The body of a process statement is actually a block, although thewords block and end block do not appear. A second block with an emptyon(others) handler is implied around this block. This is necessary so that

11. Hermes Operations 97there will always exist a target for exceptions raised within handlers of theprocess statement.The typestate on normal termination of a block statement is the meet ofthe typestates on normal exit from all the clauses which can exit normally.If none of the clauses can exits normally, the block statement can not exitnormally. The only way a clause cannot exit normally is if it ends withan exit statement or if it ends with a compound statement which itselfcannot exit normally.If compound{statement::= if test{expressionthen [ statement ; ] . . .[ else [ statement ; ] . . . ]end ifThe if statement contains an expression|the test|and two clauses|thethen and else clauses.It behaves like the if statement of conventional languages: The test ex-pression is evaluated to an initialized boolean value. If the value is 'true',the then clause is then executed; if the value is 'false', the else clauseis executed. An omitted else clause is treated as an empty else clause.The type and typestate rules are as follows: The test expression resultis inferred to have type predefined!boolean. The test expression resultmust have typestate init. The typestate on entry to either clause is thesame as the typestate after execution of the test expression. The typestateon normal exit of the if statement is the meet of the typestates for eachclause which can exit normally; if no clause can exit normally, then the ifstatement can not exit normally.Whilecompound{statement::= while test{expression repeat[ statement ; ] . . .end whileThe while statement contains an expression|the test|and a clause|therepeat clause.It behaves like the while statement of conventional languages: The testexpression is repeatedly evaluated to an initialized boolean value. If thevalue is 'true', then the repeat clause is executed, and then the whilestatement is re-executed. If it is 'false', the while statement terminates.The type and typestate rules are as follows: The test expression result isinferred to have type predefined!boolean. The test expression result ischecked to have typestate init. The typestate on entry to the test expres-

98 11.3. Control Flow Operationssion is the meet of the typestate before execution of the test expression andthe typestate on normal termination of the repeat clause. The typestateon entry to the repeat clause is the typestate after execution of the testexpression. The typestate on termination of the while clause is the sameas the typestate after execution of the test expression.Selectcompound{statement::= select [ select{expression ][ select{clause ] . . .otherwise{clauseend selectselect{clause::= boolean{guard [ statement ; ] . . .::= event{guard [ statement ; ] . . .::= event{guard and boolean{guard [ statement ; ] . . .event{guard::= event inport{variableboolean{guard::= where ( test{expression )otherwise{clause::= otherwise [ statement ; ] . . .select{expression::= expressionThe select statement consists of an optional expression, a set of selectclauses, and an otherwise clause. A select clause consists of a guard and aclause. A guard can be� a boolean guard,� a conjunction of a boolean guard and an event guard naming an inputport, or� an event guard alone, which is treated as if a boolean guard of 'true'had been coded.Example:selectwhere (x > 0)y := x + y;event inport 1

11. Hermes Operations 99receive parms 1 from inport 1;event inport 2 and where (x <= 0)receive parms 1 from inport 2;otherwiseend select;If there is an optional select expression, then it is evaluated and thevalue is stored in a temporary variable. Each boolean guard expressionis replaced by a comparison between the expression and the temporaryvariable. Example:select employee.salarywhere (low salary)employee.raise := 7;where (average salary)employee.raise := 5;where (high salary)employee.raise := 2;otherwiseemployee.raise := 0;end select;The select statement begins execution by evaluating each boolean guard(including the implicit 'true' guards). The clauses whose boolean guardsevaluated to true are said to be enabled; the clauses whose boolean guardsevaluated to false are said to be disabled.� If all clauses are disabled, the otherwise clause is executed and thenthe select statement terminates.� If one or more clauses are enabled, and all enabled clauses haveevent guards, and the input ports have no connections, then theDisconnected exception is raised.� If there is an enabled clause with either no event guard, or withan event guard whose input port is nonempty, then that clause isexecuted. If there are more than one such clause, a non-deterministicchoice is made. The choice need not be fair. That is, if the selectstatement is executed in�nitely often and if it is always possible tochoose a particular alternative there is no guarantee that this choicewill eventually be taken so long as other choices are possible.� If all enabled clauses have event guards, and at least one of the inputports has a connection, but all input ports remain empty inde�nitely,then the select statement will wait inde�nitely.In the �rst example, the second clause has an implicit true boolean guard,so it is always enabled. The �rst clause will be enabled if x > 0 and thethird clause will be enabled if x <= 0. Since some clauses will always be

100 11.3. Control Flow Operationsenabled, the otherwise clause will never be executed. If x > 0, then ifno message ever arrives at inport 1 the �rst clause must eventually beexecuted. But if a message ever arrives at inport 1 then either the �rst orsecond clause may be executed. If x <= 0, then if a message arrives onlyat inport 1, the second clause will execute; if a message arrives only atinport 2, the third clause will execute; if messages arrive at both ports,one or the other clause will execute; if a message arrives at neither port,the statement will block.In the second example, there are no event guards, and the statementbehaves like the choice statements of other languages. If low salary =average salary, then a non-deterministic choice is made between the �rsttwo clauses.Here are the type and typestate checking rules: Boolean guard expres-sions are inferred to be of type predefined!boolean. Event guards arechecked to be of type family input port.All boolean guards and all event guards must have typestate attributeinit. The typestate on entry to any clause is the typestate after evaluat-ing all guards. The typestate on normal termination of the statement isthe meet of the typestates on completion of all clauses which can exit nor-mally; if none of the clauses can exit normally, then neither can the selectstatement.Expression Blocksecondary::= evaluate declaration from [ statement ; ] . . . endAn expression block, or evaluate operator, is a way to embed statementswithin an expression. It contains a declaration of a result variable, and aclause containing statements to compute the result variable.The expression block introduces a new scope containing the result vari-able. Since expressions are not supposed to have side e�ects, all variablesdeclared outside the expression block are constant within the block.The typestate after executing an expression block is the typestate onnormal exit from the block. It is a \dead code" error if the clause does nothave a normal exit. The block must initialize the result variable.for LispObject in LispObjects where(evaluate StartsWithZ: boolean fromblock beginreveal LispObject.Pair;reveal LispObject.Pair.Car.PrintName;StartsWithZ := LispObject.Pair.Car.PrintName = Z;on (CaseError)StartsWithZ := 'false';end block;

11. Hermes Operations 101end)inspectcall LispPrint(LispObject);end for;This program fragment looks up the Lisp variable which is a pair whose�rst member is an atom with printname matching Z. The test must bewritten as an expression inside where(). To perform the test, we must writea reveal statement, so we need an expression block in order to enclose thisstatement within an expression.Exitsimple{statement::= exit exit{nameThe exit statement terminates execution of a block as if an exceptionhad been raised. In e�ect, an exit is a locally de�ned exception condition.Example:block beginif X>200000thenexit TooBig;end if;. . .on exit(TooBig). . .end block;The statement includes an identi�er which is an exit name. Controlbranches to a handler for that exit name. The handler chosen must bein a block whose main clause contains the exit statement. If there is morethan one such handler, the innermost one is chosen. (This rule is repeatedin the discussion of the block statement.)11.4 Scalar OperationsHermes has �ve scalar type families: nominal, enumeration, boolean, inte-ger, and real. The scalar types families are similar to scalar types in otherprocedural languages. In this section we �rst describe how to declare typesin these families, and then discuss the scalar operations.Scalar Type De�nitionsA nominal domain is a set of values which have no relationship other thanequality. They are used to generate distinct names|hence the term nom-inal. You can generate a new, distinct nominal value with the unique op-

102 11.4. Scalar Operationseration. You can copy nominals and test them for equality. You cannotperform ordered comparisons or conversions to integers. Two nominal val-ues are equal if they are copies of the same generated value, otherwise theyare unequal. A nominal type de�nition creates a new nominal domain.type{construction::= nominalExample:TransactionId: nominal;An enumeration domain consists of a �nite number of values. An enu-meration type de�nition de�nes a new enumeration domain, and associatesa named literal with each value, e.g. 'blue'. The named literals of a singleenumeration type de�nition must be distinct. This named literal can beused in expressions to generate the value it names. Enumeration values ofthe same type are equal if they are equal to the same named literal. Ofcourse, the value 'blue' in one enumeration domain has nothing to dowith the value 'blue' in a di�erent enumeration domain, since only valuesof the same type can be compared.type{construction::= [ ordered ] enumeration ([ named{literal [ , named{literal ] . . . ])::= variant of enumeration{type ([ case{declaration [ , case{declaration ] . . . ])Enumerations are either ordered or unordered, depending on whetherthe keyword ordered appears in the de�nition. Unordered enumerationssupport comparisons only for equality and inequality, just like nominals.Ordered enumerations also support the ordered comparisons <, >, <=, and>=. They also can be converted to integers. The �rst enumeration literalhas the converted integer value 0, the next 1, and so on.The following are some examples.option: enumeration ( 'present', 'absent' );color: enumeration ( 'red', 'blue', 'yellow', 'green' );quality: ordered enumeration ( 'poor', 'fair', 'good', 'excellent');There is a prede�ned ordered enumeration type called Predefined!Characterthat contains all the characters of the ASCII character set in sorted order.A boolean domain consists of two values, one representing \true" andthe other \false". A boolean type de�nition associates the true and falsevalues with distinct named literals.type{construction::= boolean ( boolean{association )

11. Hermes Operations 103boolean{association::= true : named{literal , false : named{literal::= false : named{literal , true : named{literalBoolean types support the same operations as unordered enumerationtypes. Additionally, the operations \and", \or", and \not" are supported.There is a prede�ned boolean type called predefined!boolean whosenamed literals are 'true' and 'false'. You can de�ne your own booleantypes with di�erent literals. Example:bit: boolean (true: '0', false: '1');An integer domain consists of the mathematical integers. Each integertype de�nition de�nes a new integer domain.type{construction::= integer::= real of accuracy integer{literal / integer{literalExample:apples: integer;oranges: integer;As usual, 5 in the apples integer domain can't be compared to 5 in theoranges domain. But you can convert between two integer domains usingthe convert operation.In some languages \integers" are de�ned modulo the size of the largestnumber that can �t in a word of computer memory. In contrast, Hermesuses the usual mathematical de�nition of integer. The result of an integeroperation is always either the mathematically correct result, or else noresult and a Depletion exception.A real domain consists of a set of safe numbers. Safe numbers are adiscrete subset of real numbers spaced su�ciently close together to meeta user-de�ned accuracy requirement. For every real number, there existsa safe number such that the relative error (the distance between the safenumber and the real number divided by the real number) is less than orequal to the accuracy requirement.The accuracy is expressed as a fraction, e.g. 1/1000000. Example:velocity: real of accuracy 1/1000000;Two other properties of approximate arithmetic often assumed by pro-grammers are supported: (1) Zero is a safe number, and (2) integers are\preferred" as safe numbers, i.e. any pair of safe non-integers with an in-teger between them must have a safe integer between them.The usual real arithmetic operations are supported; the expression syntaxis identical to that shown for integers above. Approximate real arithmeticis performed as follows. The result is computed and a safe number nearestthe exact result is used as the result. If there is more than one nearest safenumber to a number, a nondeterministic selection is made.

104 11.4. Scalar OperationsInteger, Real, and Named LiteralsLiterals are expressions which compute a result de�ned at compile-time.They are treated as nullary operations for the purpose of type-checking.Examples: 23, 'blue', "Hello, World!".You cannot tell the type of a literal just by looking at the literal|onlywhether it is an integer, real, string, or named literal. For example theliteral 2 is not necessarily of type predefined!integer. You must eitherwrite a type speci�err put the literal in a context so that the type checkercan infer the type, e.g. X <- 2. Usually, this will always be the case. Butremember that if you write an expression like 'fair' < 'good', you willget a type error. The compiler will not scan through the set of all visibletypes to see if there is a unique one in which this expression makes sense.You must use a type speci�er.Add, Subtract, Multiply, Divide, Unary-minusterm ::= factor::= { factor::= term + factor::= term { factorfactor ::= factor * secondary::= factor / secondaryExamples: A+B, -X, A*B. Addition, subtraction, multiplication, division,and negation (unary minus) are de�ned for integer and real types. Onintegers, truncating integer division is performed. Division by zero raises aDivideByZero exception.Rem, Modfactor ::= factor mod secondary::= factor rem secondaryExamples: X mod 2, Y rem Z. The remainder and modulus operators arede�ned for integers. The remainder operation a rem b produces the re-mainder of a/b. The modulus operation a mod b produces a result thathas the sign of b, an absolute value less than the absolute value of b, andthat satis�es a = bn+ (a mod b)for some integer n. Division, rem, and mod raise exception Divide-ByZero if the second operand evaluates to 0.

11. Hermes Operations 105Less, Less-equal, Greater, Greater-equalrelation ::= concat < concat::= concat > concat::= concat <= concat::= concat >= concatExamples: X = 2, Y <> 3, A <= B. The result is of boolean type (not nec-essarily predefined!boolean).And, Or, Notdisjunction::= conjunction::= disjunction or conjunctionconjunction::= relation::= conjunction and relationsecondary::= not secondaryExample: not(A and B) = not A or not B These operations are de�nedfor all boolean types. They have the usual Boolean algebra interpretation.You cannot mix operands of di�erent boolean types. But you can easilyconvert between boolean types by comparing a result to \true":blockdeclareX: boolean;Y: bit;Z: bit;begin...X := Y and Z; -- illegal because types don't matchX := (Y and Z) = '1'; -- legalend block;Uniquetyped{primary::= uniqueExample: Y <- unique. This operation generates a unique value of a nom-inal type. The value is guaranteed to compare unequal to any previouslyde�ned value of that type.

106 11.4. Scalar OperationsFor-enumeratecompound{statement::= for enumeration{variable : enumeration{type repeat[ statement ; ] . . .end forThe for enumerate statement iterates over the elements of an unorderedenumeration type. This statement consists of a declaration and a clause,where the declaration speci�es a variable belonging to an unordered enu-meration type. For every value in the domain of the enumeration type,the variable is assigned that value and the clause is executed once. Insidethe clause, the variable is required to be constant. The order in which thevalues are chosen is nondeterministic. The following is an example.for thiscolor: color repeatsend color to outport;end forConvertsecondary::= convert of secondaryThe convert of operator has only one operand. It converts values fromone ordered type to another. An example of its use is:x <- convert of y;The three ordered domains are related as follows:ordered enumeration�integer� realWhen converting between an ordered enumeration type and an integertype, the �rst named literal in the former corresponds to 0, the secondnamed literal corresponds to 1, and so on. In converting between two realtypes, the result is a safe number in the target domain nearest to thesource value. When converting from integer or ordered enumeration intoreal, the result is a safe number nearest to the integer corresponding to thevalue in the source domain. In converting from real into integer (orderedenumeration), the result is an integer (a value corresponding to the integer)nearest to the value in the source domain.If the target domain is an ordered enumeration domain, exceptionRangeEr-ror is raised when the result lies outside the domain.

11. Hermes Operations 10711.5 Record OperationsRecord Type Familytype{construction::= record ([ component{declaration [ , component{declaration ]. . . ])A record is a tuple of values called components. When a record is initial-ized, the component variables exist. The component variables may them-selves be either initialized or uninitialized. When a record is uninitialized,the component variables do not exist, are considered uninitialized, and can-not be made initialized.Two records are equal for comparison purposes when the same compo-nents are initialized and all initialized components are equal.A record type de�nition speci�es the name and type of each component,e.g.clause: record (id: clauseid,statements: statements);NewThe new operation creates an initialized record whose components areuninitialized. The previous value of the record, if any, is discarded.simple{statement::= new variable{nameNote: the two ways to initialize a record variable which is currentlyuninitialized|issue the new operation, and then initialize the components,or assign or move a pre-existing record value.11.6 Table OperationsTable Type Familytype{construction::= [ ordered ] table of element{type element{typestate[ keys [ key ] . . . ]key ::= ( [ formal{variable [ , formal{variable ] . . . ] )A table is a collection of values of the same type and typestate. The valuesare called the elements of the table, the type the element type, and the

108 11.6. Table Operationstypestate the element typestate. Tables can be used to represent bags, sets,strings, arrays, lists, and relations.A table type de�nition contains an element type and a formal typestaterepresenting an element typestate. It can contain the keyword ordered. Atable type de�nition can also contain a set of keys. Each key is written as alist of formal variables. Restrictions: (1) The variables of a key must beinitialized, (2) the element typestate must not be fully uninitialized. Theserestrictions guarantee that table elements can be compared, and that keyscan be compared.If a table has keys, any two elements of the table will have di�erent valuesof all keys. Two elements di�er in a key if they di�er in any variable of thatkey.The value of an ordered table is an ordered (possibly empty) set of ele-ments. The �rst element of the table is said to have position 0, the next 1,etc.Two ordered tables are equal if they contain equal elements in the sameorder.Two unordered tables are equal if their elements can be put in one-to-onecorrespondence and the corresponding elements are equal. The following aresome examples of table type de�nitions.intbag: table of integer finitg;string: ordered table of char finitg;string set: table of string finitg keys (*);person: record (name: string,address: string,id: integer);person table: table of person ffullg keys (name, address) (id);Type intbag is a bag|it can hold any number of integers, includingmultiple occurrences of the same integer. Type charstring is like a stringin conventional languages: it is a totally ordered bag. Type string sethas the additional speci�cation that no two strings may be equal|thusit behaves like a set. Type person table has the property that no twoelements can have the same strings for both name and address, and no twoelements can have the same value of id. This type acts like a two-waymapping between id numbers and name-address pairs.Newsimple{statement::= new variable{name

11. Hermes Operations 109The operation new creates an initialized but empty table. The previousvalue of the table, if any, is discarded.String LiteralA string literal is a self-de�ning character string value. The type of a stringliteral is an ordered table of enumeration or boolean values. Each characterin the string literal must correspond to a single-character named literal ofthe enumeration or boolean type.So if "0011010" is used as a string literal, its resolved type must be astring of a type which includes values named '0' and '1'|e.g. a typede�ned as ordered table of Bit.Concatenateconcat ::= term::= concat j termThis operation concatenates two ordered tables to produce a result con-taining a copy of the elements of the left operand followed by a copy of theelements of the right operand. The elements must be copyable.Selectorselector ::= base{variable in table{variablewhere ( selector{expression )::= base{variable in table{variable[ [ expression [ , expression ] . . . ] ]::= table{variable [ [ expression [ , expression ] . . . ] ]The selector is a syntactic construct used in several operations. There arethree syntactic forms permitted by the syntax. The �rst form is calleda long selector, the others mapping selectors. The mapping selectors aresimply abbreviations for long selectors.The variable name (table variable) is the name of a table. The base variablein a selector is called an element declaration. It de�nes a new variable namecalled the element variable, whose type is the element type of that table.This variable name is visible within the where expression. The expressionis called the selector expression. It evaluates to a result which is inferredto be of type predefined!boolean.At execution time, the selector is evaluated once for each element in thetable. The element variable is set equal to a constant copy of the element.The elements for which the selector expression are true are said to beselected.A mapping selector is a shorthand representing some common uses ofselectors. If the mapping list is empty, then all elements are selected|it is

110 11.6. Table Operationsa shorthand for where('true'). If the mapping list has a single expression,and the table is ordered, then expression is assumed to be a position and theselected element is the element at that position. Thus t[n] is shorthand fore in t where(position of e = n). If the above rule does not apply, andthe mapping list has k expressions, and one key has k variables, the k ex-pressions are assumed to be comparison values for those keys. Thus if t hastype person table, then t[x, y] is shorthand for e in t where(e.name= x and e.address = y). If none of these rules apply, or if more than onekey has the correct number of variables, the use of the mapping selector isillegal.Selectors appear as part of several operations. The use of the selectedelements depends upon the operation. The purpose of some operations isto select a single element; the purpose of others is to select an entire set.When the purpose is to select a single element, and no element is selected,exception NotFound is raised. If more than one element is selected, and thetable is ordered, the earliest element is chosen. If the table is unordered,an arbitrary choice is made.A selector by itself in an expression is the operation called the element.The result is a copy of the chosen selected element, or else NotFound israised. The result has the element type and element typestate. The elementtype must be copyable.Everysecondary::= every of selectorThe every operation returns a table containing a copy of all the selectedelements. If the original table was ordered, the copied elements are in thesame order. The result of every will be an empty table if no elements areselected. The element type must be copyable.Exists, Forallsecondary::= exists of selector::= forall of selectorThe exists operation returns a true boolean value if at least one element isselected; else it returns false. The forall operation returns a true booleanvalue if all elements are selected; else it returns false.Insert, Insert-atsimple{statement::= insert element{expression into table{variable[ at position{expression ]

11. Hermes Operations 111The insert statement adds a new element to a table by moving in a valuefrom a source variable. The source variable must be of the element type.The value must have the element typestate. If it has a lower typestate, theoperation is illegal. If it has a higher typestate, the typestate is lowered. Ifthe table is ordered, the element is inserted at the end of the table. If thetable is keyed, the DuplicateKey exception is raised if the insertion wouldhave violated the requirement of unique keys.The insert-at statement is legal only for ordered tables. It behaves ex-actly like insert, except it has an additional operand of type predefined!integerwhich speci�es where to insert the element. If this position is negative, orgreater than the number of elements in the table, the exception RangeErroris raised. Otherwise, the element is inserted at the designated position. Thepositions of earlier elements in the table remain the same. The positions oflater elements (if any) are increased by one. The exception DuplicateKeymay be raised exactly as for insert, except that a RangeError exceptiontakes precedence.After either insert or insert-at, the element variable is uninitialized,since its value is moved and not copied.Removesimple{statement::= remove element{variable from selectorThe remove statement has two operands: a destination element, which ap-pears after the word remove, and a selector. The chosen selected element isremoved from the table and moved into the destination variable. If there isno selected element, the exception NotFound is raised. The previous value,if any, of the destination variable is discarded.Examples:remove Char from String[0];remove Min from I in IntBagwhere(forall of J in Intbag where(J >= I));The �rst statement removes the �rst character from the string, if there isone. If there is none, an exception is raised. The second statement removesthe smallest integer from Intbag|the integer I such that all integers inIntbag are at least as large as I. It will raise an exception if the table isempty.Extractsimple{statement::= extract table{variable from selectorThe extract statement has two operands: a destination table, whichappears after the word extract, and a selector. All chosen selected ele-

112 11.6. Table Operationsments are removed from the table named in the selector, and moved intothe destination table. If no elements are selected, the destination table isinitialized to an empty table. The previous value, if any, of the destinationis discarded.Merge, Merge-Atsimple{statement::= merge table{expression into table{variable[ at position{expression ]The merge statement moves the contents of the source table into the des-tination table. The two tables are of the same type|meaning that theelements will be of the same type and typestate. On normal completion,the source variable will be uninitialized. If the table is ordered, the ele-ments (if any) from the source table will be inserted at the end of thedestination table, and their order will be preserved. If the table is keyed,the DuplicateKey exception is raised if the merge would have violated therequirement of unique keys.The merge-at statement is legal only for ordered tables. It behaves ex-actly like merge, except it has an additional operand of type predefined!integerwhich speci�es where to insert the source table elements. If this positionis negative, or greater than the number of elements in the table, the ex-ception RangeError is raised. Otherwise the elements are inserted at thedesignated position. The positions of earlier elements in the table remainthe same. The positions of later elements (if any) are increased by the num-ber of elements merged. The exception DuplicateKey can be raised exactlyas for merge, except that a RangeError exception takes precedence.Inspect-tablecompound{statement::= inspect selector begin[ statement ; ] . . .end inspectThis statement makes a constant copy of the chosen selected element of atable. This copy is stored into a new variable { the inspect variable havingthe same name as the element variable of the selector. The inspect variableis automatically declared to be of the element type of the table, and to havea scope including the clause within the body of the inspect statement. Ifthere is no selected element, the NotFound exception is raised.Whereas it is illegal to make ordinary copies of certain values (callmes-sages, input ports, or values containing them), it is always legal to makea constant copy of any value. This is because the operations which wouldbe \dangerous" (receive, return) when performed on ordinary copies are

11. Hermes Operations 113not allowed on constant copies. A variable holding a constant copy is auto-matically made uninitialized when control leaves the scope of the variable.The following is an example of the inspect statement.inspect char in stringwhere (char = 'X' and position of char > 2)begininsert copy of char into string2;end inspect;Note the double use of the variable char|(1) within the where expres-sion as the element variable of a selector, and (2) within the insert state-ment as the variable holding the constant copy. Note the di�erences be-tween the inspect statement and the similar statement using the operationthe-element.c := char in string where(char = 'X' and position of char > 2);insert c into string2;The inspect statement introduces a declaration of char, whereas cmusthave been previously declared. The inspect statement works for all types,whereas the-element works only with copyable types. The inspect state-ment produces a constant copy, whereas the-element produces an ordinarycopy. It is illegal to apply insert directly to char because it is constant,but it is legal to apply insert directly to c. The position-of-elementoperation can be applied to constant copies but not to ordinary copies.The typestate on entry to the main clause of the inspect statementis the same as the typestate prior to the inspect statement except thatthe inspect variable is in the element typestate. The typestate on exitfrom the inspect statement is the same as the typestate on exit from themain clause, except that the inspect variable becomes uninitialized. If themain clause cannot exit normally, then the inspect statement cannot exitnormally.For-Inspectcompound{statement::= for selector inspect[ statement ; ] . . .end forThe for-inspect statement iterates over the elements of a table or a se-lected subset of the table. The table must be initialized. The main clause(the clause following inspect) is executed once for each selected element.A variable with the same name as the element variable of the selector holdsa constant copy of the selected element. The value of the table on entryto the for-inspect statement is used to determine the selected values;any changes to the table made from within the iterated clause will not af-fect which values are selected. If the table is ordered, the elements will be

114 11.6. Table Operationsselected in that order.The typestate on entry to the main clause of a for-inspect statement isthe meet of (1) the typestate in which the inspect variable is in the elementtypestate and other typestate attributes are the same as they were beforethe for-inspect statement, (2) the typestate at the end of the main clause,assuming it can exit normally. The typestate on exit from the for-inspectstatement is the meet of the typestate on entry to the main clause and thetypestate prior to the for-inspect statement. A for-inspect statementcan always terminate normally, even if the main clause terminates in anunconditional exit, since it is possible for the main clause to be executedzero times.Sizesecondary::= size of secondaryThe operator size of evaluates to an integer containing the number ofelements in the table. The table must be initialized.Position-of-elementsecondary::= position of element{variableThis operation can be applied only to constant copies of elements selectedfrom an ordered table. These constant copies are produced only within awhere expression of a selector, an inspect, or a for-inspect statement.The operation evaluates to an integer which is the position of the selectedelement which was used to produce the constant copy stored in the elementvariable. Positions begin at 0.Example:string <- "abcabd";inspect c in string where(c = 'b')beginp <- position of c;end inspect;The variable p will receive the value 1|the position of the �rst 'b' inthe string.Position-of-selectorsecondary::= position of selectorThis operation searches an ordered table for the �rst selected element. Itevaluates to an integer equal to the position of this selected element. If

11. Hermes Operations 115there is no selected element, a NotFound exception is raised.11.7 Variant OperationsVariant Type Familytype{construction::= variant of enumeration{type ([ case{declaration [ , case{declaration ] . . . ])case{declaration::= named{literal ! component{declarationcomponent{typestateA variant is a value which will have one of a �xed, pre-speci�ed set oftypes. Since in Hermes, every variable name has exactly one type, we usea di�erent component name to designate each di�erent type of value. Avariant V can be either� uninitialized: it has no value and there are no typestate attributesinvolving variable V� initialized and hidden: exactly one component exists. The programdoesn't know which component exists and which do not, so no com-ponent is accessible. The typestate is init(V).� initialized and revealed: exactly one component has a value. The pro-gram knows which component has a value and can access that com-ponent and no other. The typestate is init(V), case(V, V.X). Theremay be other typestate attributes involving variable V.X.Two variants are equal if they have the same case and if their existingcomponents are equal.The components of a variant type are in one-to-one correspondence withthe named literals of an enumeration type. These named literals are calledthe cases of the variant, and the enumeration type is the case type.A variant type de�nition speci�es: (1) the case type, (2) for each namedliteral of the case type, a component name, component type, and a formalcase typestate. The case typestate is the typestate the component will havejust before it is hidden and just after it is revealed. While the variant isrevealed, the typestate may become higher than this typestate, but neverlower.The following is an example of a variant type de�nition.id : charstring;lisptype: enumeration ('nil', 'atom', 'pair');

116 11.7. Variant Operationss expression: variant of lisptype ('nil' -> null: empty fg,'atom' -> atom: id finitg,'pair' -> pair: cons cell ffullg);cons cell: record (car: s expression,cdr: s expression);Note that these type de�nitions are mutually recursive|variables of types expression contain components of type cons cell, which in turn con-tain components of type s expression.In this example, if V is a variable of type s expression, and it has justbeen revealed to be in case 'pair', then the typestate will beinit(V), case(V, V.Pair), init(V.Pair), init(V.Pair.Car), init(V.Pair.Cdr)Inner components, such as V.Pair.Car.Pair.Cdr, will not be accessibleuntil they are revealed in turn. It is not legal to discard V.Pair.Car, leav-ing a partially initialized pair. It is, however, legal to replace the value ofV.Pair.Car.Unitesimple{statement::= unite variant{component from source{expressionThe unite statement initializes a variant to a particular one of its cases.Its two operands are a variable name designating a variant component,and an expression evaluating to a value of the same type as that variantcomponent. The typestate of the expression must be at least as high as thecase typestate of the variant component. If it is higher, it is coerced untilit is exactly equal to the case typestate.If the variant had a value, that value is discarded. The value of theexpression is then moved into the variant component. After the operation,the variant will be revealed and the variant component will be accessible.Dissolvesimple{statement::= dissolve variant{component into result{variableThe dissolve statement reverses the e�ect of the unite statement. The�rst variable name designates a variant component. The second variablename designates a variable of the same type which will receive the value ofthat variant component.The variant component must be revealed. Any previous value of thedestination variable is discarded. The value of the variant component is

11. Hermes Operations 117then moved into the destination variable. After the operation, the variantwill be uninitialized, and the destination variable will have the typestateattributes of the variant component.Revealsimple{statement::= reveal variant{componentThe reveal statement reveals a hidden variant component. The operandis a variant component. The variant must be hidden|if it is revealed, it iscoerced back to hidden.If the case of the variant is such that the component being revealedexists, then that component is revealed. If the case of the variant is suchthat the component being revealed does not exist, the exception CaseErroris raised, and the variant remains hidden.Hidesimple{statement::= hide variant{variableThe hide operation is a coercion. It can be explicitly coded, or (moreusually) generated automatically as a result of a coercion which drops thecase attribute.The single operand of a hide statement is a variable of variant type.It must be initialized. After executing the statement, the variant will behidden.Casesecondary::= case of secondaryThe case of operator takes a variant as operand and returns the case ofthe variant. The result is a value of the variant's case type.

118 11.7. Variant Operations11.8 Process Creation and CommunicationOperationsInput Port, Output Port, Callmessage Type Familiestype{construction::= callmessage ([ component{declaration [ , component{declaration ]. . . ])[ constant{parameters ]exit exit{typestate[ minimum ][ user{exception ] . . .::= inport of callmessage{type entry{typestate::= outport of inport{typeconstant{parameters::= constant ( [ component{name [ , component{name ] . . .] )minimum::= minimum minimum{typestateuser{exception::= exception user{exception{name exception{typestateThe type families input port, output port, and callmessage are used in\programming-in-the-large"|that is, the division of systems into processes,the creation of processes, the creation of bindings between processes, andthe communication between processes.An input port or inport is a message queue to which connections can bemade. It should be emphasized that in Hermes inports are values, and thatinport values are stored in inport variables just as integer or string valuesare stored in integer or string variables. This is completely consistent withthe rest of Hermes, but might appear \di�erent" because in many languagesports or entries are not values of variables.An inport type de�nition speci�es (1) a type name, called the messagetype, and (2) a formal typestate, called the entry typestate.Usually the message type will be a callmessage type. However, the mes-sage type can be any type, since send can be use to transmit any value toan inport.The entry typestate is the typestate that values of the message typewill have when sent. You may omit init(*) from the formal typestate of acallmessage, since all call statements will generate initialized callmessages,and using send to deliver an uninitialized callmessage is pointless.

11. Hermes Operations 119Two inports compare equal only if they are the same inport or constantcopies of the same inport. comparison ofAn output port or outport is a connection to an input port. As with inputports, it should be re-emphasized that output ports are treated like anyother Hermes value, and can be stored in variables and passed in messages.It is the ability for programs to send and receive output ports that givesHermes the power of capability-based systems.Two outports are equal when they are connections to the same inputport. Each input port created with the new operation is considered a dif-ferent input port. A constant copy of an input port is considered the sameinput port.An outport type de�nition speci�es a matching input port type. This isthe type of the input port to which the output port can be connected. Thetype and typestate of data sent on an output port is determined by themessage type and typestate of this matching input port type.A callmessage type de�nition consists of� a set of component declarations,� an optional set of constant parameters,� an exit formal typestate,� an optional minimum formal typestate, and� an optional set of callmessage exceptions.The component declarations de�ne the call parameters. The order ofthese declarations is important, because the call arguments are matchedto these parameters in the same order. The constant parameters state whichcomponents are not modi�able by the called process. The exit formal type-state speci�es the typestate of the callmessage when it is returned to thecaller in a normal outcome. The minimum formal typestate speci�es thelowest typestate that the components of a callmessage may be lowered to.This is the typestate to which the components will be lowered when thecallmessage is discarded.The callmessage exceptions consist of an exception name and a formaltypestate. The formal typestate speci�es the typestate that the callmes-sage will have when it is returned with that exception. Do not include theDiscarded exception in this list| this exception is always implicitly de-clared and the typestate for this exception is the same as the minimumtypestate.If there is no explicit minimum formal typestate, then it is taken to beone in which all constants are fully initialized, and all other parameters areuninitialized.The following is an example of a callmessage and the associated ports.sample interface: callmessage (

120 11.8. Process Creation and Communication Operationsfirst: integer,second: integer,third: integer,fourth: integer)constant (first)exit finit(first), init(second), init(fourth)gminimum finit(first), init(second)gexception Failure finit(first), init(second), init(third), negative(first)g;sample inport: inport of sample interfacefinit(first), init(second), init(third)g;sample outport: outport of sample inport;In this example, parameter first is an in parameter|initialized onentry and exit, and guaranteed constant. Parameter second is an inoutparameter|initialized on entry and exit, but not guaranteed constant. Pa-rameter fourth is an out parameter|uninitialized on entry, but returnedinitialized on normal exit. Parameter third is a transferred parameter|owned by the caller before the call, but retained by the called processand returned uninitialized. The typestates for the exception Failure aredi�erent|parameters first, second, and third will be initialized, andparameter first will be known to be negative.It is illegal for the entry typestate of an inport whose message type isa callmessage to specify a typestate which is not higher than or equal tothe minimum typestate. Similarly all exit and exception typestate must behigher than or equal to the minimum typestate.Newsimple{statement::= new variable{nameThe new operation initializes an input port variable to an empty queue withno connections. The previous value of the input port variable is discarded.Emptysecondary::= empty of secondaryThe empty of operation returns a true boolean value if the queue is empty,otherwise it returns false. The operand must be an initialized inport.Since other processes may enqueue messages at arbitrary times, beingempty is not a stable property; a process may repeat a successful emptyof operation only to �nd the inport no longer empty. However, since onlythe process owning an input port can receive messages, being non-empty isa stable property.

11. Hermes Operations 121Connectsimple{statement::= connect outport{variable to inport{variableThe connect statement has two operands. The �rst operand (before thekeyword to) is an outport variable. The second is an inport variable. Theinport variable must be of the matching inport type of the outport variabletype, and must be initialized.The previous value of the outport variable, if any, is discarded. Theoutport variable is assigned to a connection to the inport which is thecurrent value of the inport variable.Callsimple{statement::= call outport{variable call{arguments::= call ( outport{expression ) call{argumentsfunction{reference::= outport{primary call{argumentsassociationassociated{pairThe call statement corresponds to a procedure call in conventional lan-guages. The operands of the call statement are an outport and a list ofarguments. The outport must be initialized. The matching inport type musthave an element type which is a callmessage. The arguments are matchedup to the callmessage components either by position, if positional notationis used, or by name, if association notation is used. If function notation isused, then there should be one fewer argument than parameter, the miss-ing parameter being the result, which is passed as an unnamed variable. Ifpositional notation is used with a function call, then the last callmessagecomponent is the result. If association notation is used, then the omittedcomponent is the result.Each argument must have a type matching its corresponding parameter.Each argument must have a typestate at least as high as the entry typestateof the corresponding parameter. The typestate of arguments matching non-constant parameters will be lowered to exactly match the entry typestateof the corresponding parameter. The typestates of arguments matchingconstant parameters will remain unchanged.Overlapping variables cannot be passed as two or more arguments of acall. Two variables overlap if one of their names is a pre�x of the other orthey are the same.

122 11.8. Process Creation and Communication OperationsIt should be noted that expressions are evaluated into temporary vari-ables. If these variables are passed to modi�able parameters, the modi�ca-tions are lost. (For example, if you pass 3 to an inout parameter, and theprocedure returns 4, you will not change the value of 3.) Since function callsare parts of expressions, and expressions may not modify their operands,no function call can have any modi�able parameter except its result.The call is executed by: (1) creating a new callmessage value; (2) mov-ing the arguments into the callmessage, (3) sending the callmessage tothe input port to which the output port is connected, (4) waiting for thecallmessage to be returned, (5) moving the callmessage components backinto the argument variables, (6) throwing away the callmessage.If several output ports are connected to the same input port, then callson these output ports are merged fairly. This means that if a call or sendis made, and the receiving process repeatedly issues receive statements,then eventually the message will be received.If the input port has been thrown away, leaving the output port withouta port connection, the Disconnected exception is raised. The argumentsare returned in the same typestate they were sent.If the call is returned with an exception, then all the above steps are car-ried out as in a regular call, but additionally, the exception is raised in thecaller. This can only happen if there exists a declaration of that exception inthe callmessage type de�nition. The full name for the exception includes thecallmessage type and the exception name|<cmtype>.<exceptionname>.If the process owning the callmessage discards it, then the exception<cmtype>.Discarded is raised in the caller. The callmessage is returnedwith the parameters in the minimum typestate as de�ned in the callmessagede�nition.The typestate after executing a call statement is derived from the types-tate just prior to the call, from the entry typestate, and from the typestatede�ned for the normal or exceptional return being made. The di�erencebetween the entry and return typestates de�nes which attributes of theparameters are added and dropped. These adds and drops are applied tothe arguments matching the parameters.For examplecall p(a, b, c, d);Suppose the type of p is sample outport, de�ned above. Suppose thetypestate prior to the call is: finit(p), init(a), even(a), init(b),even(b), init(c)g. Since a matches the constant parameter first, itmay retain its additional attribute even(a). Since b matches the non-constant parameter second, it cannot retain its attribute even(b), whichis dropped prior to the call. On normal return from the call, the typestatewill be finit(p), init(a), even(a), init(b), init(d)g. On excep-tion sample interface.Failure, the typestate will be finit(p), init(a),even(a), negative(a), init(b), init(c)g.

11. Hermes Operations 123Receivesimple{statement::= receive callmessage{variable from inport{variableThe receive statement dequeues a message from an input port, if this ispossible, and stores it in a destination variable. The input port must beinitialized.On normal completion of receive, a message is dequeued from the in-port. The previous contents of the destination variable, if any, are discarded.The message is moved into the destination variable. This variable will be inthe entry typestate associated with the inport. The choice of which messageto receive is non-deterministic.If the inport has no connections, then the Disconnected exception willbe raised.If the inport has connections, but no message is ever sent to the inport,then the receive statement will block inde�nitely.Returnsimple{statement::= return callmessage{variable[ exception user{exception{name ]The return statement returns a callmessage to its caller and resumes exe-cution of the caller. Optionally, an exception can be returned to the caller.The return statement must specify either no exception, or a legal ex-ception for that callmessage type|either Discarded, or an exception listedin the de�nition. The exception name is written without any type namequali�er.The callmessage must have the typestate attributes associated with thekind of return|the exit typestate for a return without exceptions, or theappropriate exception typestate for a return with an exception. If the type-state is higher, coercions are inserted to lower the typestate to exactly thatwhich is expected by the caller.After the return, the callmessage is uninitialized. The caller resumesexecution. If an exception is speci�ed, that exception is raised in the caller,not in the process issuing the return statement. The process issuing returncontinues executing.There is no automatic termination of a process executing a return state-ment, as would be the case if processes were passive procedures. To producethe equivalent e�ect of procedures, code the return statement as the lastexecutable statement of the process, or follow the return statement withan exit statement jumping to the end of the process.

124 11.8. Process Creation and Communication OperationsSendsimple{statement::= send source{expression to outport{expressionThe send statement moves a value out of a variable and enqueues it on aninput port. Its operands are a source variable and an outport.The variable whose value is to be sent must have the correct type, andmust have the entry typestate or higher. The type and typestate are de-termined from the matching inport type of the outport. If the typestate ishigher than the entry typestate, it is coerced down to the entry typestate.The output port must be initialized.If the input port to which the output port was connected has been dis-carded, then the Disconnected exception is raised.Createsecondary::= create of secondaryThe create of operation instantiates a new process. The operand of createis a variable of type predefined!program. The result is an outport.The program must have the typestate checked.The program value is de�ned in the predefined module. It consists of aset of processes. One is the main program. The others are process literalscompiled within the main program or within other process literals. A mainprogram is the result of converting the process module production of asource module.The initialization port is the variable declared following the keywordprocess. This must be an input port type. When create is executed, it ischecked that this input port type is the matching input port type of theoutport type which is the result of the create statement. If it is not, theexception InterfaceMismatch is raised.If there is no interface mismatch, a new process is created. This processwill execute the program de�ned by the main program in the program value,beginning at the statement following the keyword begin. Initially only theinitialization port will be initialized. A connection to the initialization portis returned as the value of create.Proceduresecondary::= procedure of secondaryThe procedure of operation instantiates a process generator. The operandis a checked program, and the result is an output port. As with create,an InterfaceMismatch exception is returned if the output port type is

11. Hermes Operations 125incompatible with the input port type of the initialization port of the newprocess.The e�ect of procedure is identical to that of create except that whatis created is not a process running the designated program, but instead isa generator process.Each time the generator process is called, it constructs a new instance ofa process running the designated program. It then sends the callmessage itreceived to the initialization port of the new process.Ordinary processes are not recursive, since an attempt by a process tocall an input port in the same process will produce a deadlock. A call to agenerator process will not deadlock since each call generates a new instanceof the designated program. The operation to create a generator processis called procedure of because the semantics of generator processes isexactly like the semantics of procedures in Algol-like languages.11.9 Polymorph OperationsPolymorph Type Familytype{construction::= polymorphA polymorph is a value consisting of two parts: (1) a wrapped value, whichcan be of any type and typestate, (2) a wrapper describing the type andformal typestate of the wrapped value.A polymorph type de�nition de�nes a distinct domain of polymorphs forthe purposes of assignment and comparison. For example:Resource: polymorph;MailItem: polymorph;Variables of type Resource and variables of type MailItem can containwrapped values of any type; however, a variable of type Resource can notbe assigned with or compared to a variable of type MailItem.Two polymorphs can be compared for equality. The polymorphs are equalif and only if both wrappers and both wrapped values are equal.Wrapsimple{statement::= wrap source{expression as polymorph{variableThe wrap statement removes a value from a source variable, adds a wrappercontaining the source variable's type and formal typestate, and moves theresultant polymorph value into the polymorph variable. The polymorphvariable itself will have a typestate of init after the operation, regardlessof the typestate of the wrapped value. The source variable's typestate will

126 11.9. Polymorph Operationsbe uninitialized. Before the wrap statement typestate analysis will drop anyconstraint which include both a variable that overlaps the source variableand a variable that does not.Other typestate restrictions apply as for a move statement: neither sourcenor destination can be constant, and attributes other than init involvingthe destination variable will be dropped.For example, suppose that variable x is of type person, and the types-tate attributes including x are: finit(x), init(x.name), init(x.id),positive(x.id), gt(x.id, y)g. Then after executing the statementwrap x as y;the value of y will be a polymorph whose wrapped value is the orig-inal value of x, and whose wrapper contains a type of person and aformal typestate of finit(*), init(name), init(id), positive(id)g.The constraint fgt(x.id, y)g will be dropped.The new typestate for y will be simply finit(y)g.Unwrapsimple{statement::= unwrap result{variablefrom polymorph{expression unwrapped{typestateThe unwrap statement removes a wrapped value from a polymorph variableand moves the value into a destination variable. A runtime check insuresthat the type and typestate on the wrapper match the type and typestateexpected by the destination variable. The source (polymorph) variable mustbe initialized.The expected type is simply the type of the destination variable. Theexpected typestate is speci�ed by coding a formal typestate within theunwrap statement.If the type on the wrapper does not match the destination variable'stype, or if the typestate on the wrapper is not at least as high as the formaltypestate speci�ed on the unwrap statement, then a PolymorphMismatchexception is raised.If the typestate on the wrapper is higher than the typestate speci�ed onthe unwrap statement, then the appropriate attributes are dropped beforemoving the value. This may entail discarding some of the wrapped value.Other typestate restrictions apply as for a move statement: neither sourcenor destination can be constant, the source variable becomes uninitialized,attributes of the destination variable other than those de�ned by the formaltypestate are dropped.For example, assume that immediately after the wrap statement shownabove, we code the statement:unwrap x from y finit(*), init(id), positive(id)g;

11. Hermes Operations 127The types will match, but the formal typestate in the wrapper of ywill include finit(name)g. Component name will be discarded before mov-ing the wrapped value into x. The resulting typestate will be finit(x),init(x.id), positive(x.id)g.Inspect-polymorphcompound{statement::= inspect declarationfrom polymorph{expression inspect{typestatebegin[ statement ; ] . . .end inspectThe inspect-polymorph statement behaves like an unwrap statement ex-cept that instead of moving the wrapped value, a constant copy is made.This allows the value of a polymorph to be examined, even if the poly-morph is constant.Here is an alternative version of the unwrap statement shown above,which would be legal even if the polymorph y were constant:inspect x:person from y finit(*), init(id), positive(id)gbegin... /* statements referring to x */end inspect;Assuming that the wrapped value had an initialized component name,that component would not be discarded in the constant copy x, but itwould not be accessible, since the typestate inside the statement would besimply finit(y) init(x), init(x.id), positive(x.id)g.The exception PolymorphMismatch is raised exactly as for an unwrapoperation.Typesecondary::= type of secondaryThe type of operation evaluates to the type information stored in the poly-morphwrapper. This is an initialized value of type predefined!typeof value.The value includes the type name, the de�nitions module de�ning the typename, and all the imported de�nitions modules.The operand must be an initialized polymorph.Typestatesecondary::= typestate of secondary

128 11.9. Polymorph OperationsThe typestate of operation evaluates to the typestate information storedin the polymorph wrapper. This is an initialized value of type predefined!typestateof value.The value includes the typestate, the de�nitions module de�ning the type-state, and all the imported de�nitions modules.The operand must be an initialized polymorph.11.10 Program OperationsIn Hermes, you can create programs in two ways: You can write them inlinear source form and pass them to the Hermes compiler for conversioninto checked program values. Alternatively, you can create program values,either \from scratch", or by composing or modifying other program values.A program value obtained from the compiler will have the typestate at-tribute predefined!checked. A program built from scratch or by modify-ing a checked program will be missing the predefined!checked attribute.To check a fully initialized instance of program variable pgm, execute thestatementassert checked(pgm);In most implementations of Hermes, checking a program also entails com-piling it into machine code for the machine or machines on which the pro-gram will be instantiated. This is an optional and machine-dependent op-timization; the programmer cannot directly manipulate the machine code.The checking, however, is not optional. It is a typestate error to try toinstantiate a program which is not checked.The predefined!program de�nition is shown in the appendix.The operations in this section are used to construct objects of typepredefined!program.Program Literalprogram{literal::= process ( declaration )[ pragma ][ declaration{section ]begin[ statement ; ] . . .[ handler ] . . .end process::= process link{nameA program literal operation is coded as a self-de�ning value. The value ofthe literal is the value of the program after translation by the compiler.The compiler will check the program literal at the same time that it checks

11. Hermes Operations 129the program which embeds it. Therefore the program literal value will havethe typestate attribute checked.Example:doubler <- create of process (y: arithQ)declareinit: arith;beginreceive init from y;init.result <- 2 * init.input;return init;end process;The program literal is compiled using the using and linking list ofthe module in which it is embedded. However, the names de�ned withinthe program literal (e.g., variables, exits) are totally separate from namesde�ned outside the program literal. In this example, this means that thereis no con ict between the variable names y and init and any occurrencesof these identi�ers outside the program literal. Unlike \nested procedures"of Algol-like languages, Hermes program literals cannot reference variablesfrom the enclosing context.Hermes provides an alternative form of the program literal so that oneliteral can be separately compiled and shared by many programs.linking( factorial )factorialFn <- create of process factorial;Process factorial must have been previously compiled. Including itsmodule-name in the linking list enables the compiler to �nd the compiledcode and incorporate the process literal in this process.Currentprogramtyped{primary::= currentprogramThe operation currentprogram evaluates to a checked program value whichwhen instantiated will execute the process that initially invoked currentprogram.This operation can be used to create recursive programs:factorial: using( arith ) process(init: arithQ)declareparm: arith;factorial: arithFn;beginreceive parm from init;selectwhere(parm.input < 0)return parm exception InputError;

130 11.10. Program Operationswhere(parm.input = 0)parm.result <- 1;return parm;where(parm.input > 0)factorial <- create of currentprogram;parm.result <- parm.input * factorial(parm.input - 1);return parm;otherwiseend select;end processAttributename Literalabsprog{literalThe attributename literal evaluates to a fully initialized value of typepredefined!attribute name.For example:a: formal attribute;...new a:a.attribute name <-attributename init; new a.parameters;Typename Literalabsprog{literalThe typename literal evaluates to a fully initialized value of type predefined!typename.It is used either in building programs, or in checking polymorph wrappers.d: declaration;...new d;d.id <- uniqueid;unite d.typename.typename fromtypename predefined!integer; new d.prag;Checkde�nitionssimple{statement::= checkde�nitions de�nitions{module{variableagainst de�nitions{library{variable

11. Hermes Operations 131This operation completes the initialization of a de�nitions module. Theoperand is a fully initialized de�nitions module. The de�nitions moduleis checked for resolution errors and other restrictions (e.g. each variantde�nition includes a case id for each possible value of the enumerationtype). If the check fails, the exception DefinitionError is raised.If the check succeeds, the id component of the de�nitions module is re-placed by a unique identi�er. All references to that identi�er within the def-initions module are replaced by the unique identi�er. The de�nitions mod-ule acquires the attribute checkeddefinitions.The checkeddefinitionsattribute behaves like a constraint: any attempt to modify the de�nitionsmodule will discard the attribute checkeddefinitions.Unlike a constraintattribute, checkeddefinitions cannot be used in an assert statement;the attribute can only be asserted by executing the checkdefinitionsstatement.Because checked de�nitions are given a unique module identi�er, Her-mes can enforce \name equivalence" of types. No other checked de�nitionsmodule can possibly have the same module identi�er. Therefore it is guar-anteed that when two typenames or attribute names are equal, they alwaysdenote the same type or attribute de�nition.11.11 Constraintsattribute{construction::= constraint ([ declaration [ , declaration ] . . . ])is constraint{typestate constraint{expressionConstraints are programmer-de�ned predicates on the values of variables. Itis often desirable to include constraints in interfaces or in type de�nitions.For instance, the interface to a square-root function might require that theargument be non-negative. A program library might be de�ned as a tableof checked programs.In Hermes, the programmer can specify that certain constraints are tobe tracked as typestate attributes. Currently, Hermes is not sophisticatedenough to understand the semantics of constraint predicates well enoughto track how they are preserved under all operations. For example, thecompiler will not determine that adding one to a non-negative number willyield another non-negative number. What it will do is the following:� associate a name with a predicate { e.g. NonNegative(x) with thepredicate x >= 0. This de�nes an operation assert NonNegative(a)which will test whether a >= 0 and raise an exception if it is not.� add the typestate attribute NonNegative(a) if the operation assert

132 11.11. ConstraintsNonNegative(a) succeeds.� track the attribute NonNegative(a) through successor statementsuntil either a is modi�ed, or control merges with a path in whichNonNegative(a) is not in the typestateThis simple tracking of constraints is enough for many practical purposes.Constraint De�nitionsA constraint de�nition includes: a de�ning occurrence of an attribute name,a set of formal parameters, the type and typestate of these formal param-eters.Example:NonNegative: constraint(x: integer) isfinit(x)g x >= 0;Interval: constraint(x: integer, y: integer) isfinit(x), init(y)g x <= y;Constraint attribute de�nitions occur in de�nitions modules and are im-ported exactly as are type de�nitionsAssertsimple{statement::= assert attributeThe assert operation evaluates the predicate de�ned by the attribute nameand arguments. The programmer must supply the same number of argu-ments as the attribute has formal parameters. The type of each argumentmust match the type of the corresponding formal parameter. The type-state attributes in the attribute de�nition must be present at the pointwhere the assert statement is executed. The required attributes are de-termined by substituting the arguments on the assert statement for theformal parameter variables in the constraint typestate.If the predicate evaluates to 'true', then the assert statement exitsnormally. The typestate on normal exit will include the attribute men-tioned in the assert statement. If the predicate evaluates to 'false', theConstraintError exception is raised. If an unhandled exception occurswhile evaluating the predicate, then the ConstraintFailure exception israised.Dropsimple{statement::= drop attribute

11. Hermes Operations 133The drop statement explicitly drops a constraint attribute. This operationis usually generated as a coercion when merging two paths one of whichincludes a constraint attribute and the other of which does not. However,it can be coded explicitly by the programmer to indicate that a constraintis about to be broken:assert Invariant(Database);while('true') repeatRq <- GetTransaction();drop Invariant(Database);... /* make changes to database */assert Invariant(Database);end while;

Appendix AHermes Concrete SyntaxThe Hermes syntax is presented in this appendix in a format similar to thefamiliar BNF (Backus-Naur Form). The following notational conventionsapply:� Names of syntactic categories (nonterminal symbols) are rendered initalic. Example: compound{statement� Keywords and other non-terminals (e.g. punctuation) are rendered inbold face. They should appear in programs exactly as shown (thoughcase is insigni�cant).Example: begin, <>, !� Optional syntax is enclosed in square brackets. The indicated con-struct may appear once or not at all.[ declaration{section ]� Repeating syntax is indicated by ellipsis. The immediately precedingconstruct may appear any number of times in succession. Note thatif the repeated construct is not shown as optional, then at least onerepetition must appear.Example: exit{name [ , exit{name ] . . .

Appendix A. Hermes Concrete Syntax 135A.1 Lexical RulesWe begin with the rules that de�ne the lexical tokens of the language,including identi�er and various types of literal. Note that keywords andreserved words are lexically equivalent to identi�ers. A table is used todistinguish them from normal identi�ers. Additional logic is required todetermine when a keyword is actually used as a keyword and when it isused as a normal identi�er, since most keywords are not reserved. Keywordsare listed in Table A.1.Certain characters and two-character combinations constitute punctua-tion tokens. Punctuation sequences are listed in Table A.2.Spaces and tabs are not allowed within tokens, except in the case ofnamed{literal and string{literal, where they treated like any other char-acter. Tokens may never span lines, including named literals and stringliterals. Arbitrary amounts of whitespace (spaces, tabs, line breaks) mayappear between tokens.There are two commenting styles:� Two consecutive hyphens (--) begin a comment that extends to theend of the line.� Comment text may be embedded within a line by delimiting it withthe character pairs \/*" at the beginning and \*/" at the end.A comment begins with two consecutive hyphen characters (--) andextends to the end of the line. A comment is treated as a space.identi�er ::= alpha [ alphanum ] . . .integer{literal::= [ sign ] digit [ digit ] . . .real{literal ::= [ sign ] initeger{part . [ fraction{part ] [ exponent ]integer{part::= integer{literalfraction{part::= integer{literalexponent ::= exponent{letter [ sign ] integer{literalnamed{literal::= ' named{literal{character [ named{literal{character ] . . . 'string{literal::= " [ string{literal{character ] . . . "Character classes used in the above are as follows. Nonprinting charactersother than the space character are implicitly excluded in all cases.

136 A.1. Lexical Rules� alpha is any upper or lower case letter, or the underscore character( ).� digit is any of the digits 0 through 9.� alphanum includes all characters in digit and digit.� sign includes the plus (+) and minus (-) signs.� exponent{letter includes the upper and lower case letter 'E'.� named{literal{character includes all printing characters except thesingle quote ('). Two consecutive single-quote characters within anamed{literal are treated as one single quote character in this class.� string{literal{character includes all characters except the double quote("). Two consecutive double-quote characters within a string{literalare treated as one double quote character in this class.

Appendix A. Hermes Concrete Syntax 137accuracy drop is remagainst else y keys removeand y empty linking repeatas end y merge returnassert enumeration minimum revealat evaluate y mod selectattributename event y moduleid sendbegin y every new sizeblock except nominal tableboolean exception not y then ycall exists of tocallmessage exit on y truecase extract or typecheckde�nitions false ordered typenameconnect for otherwise y typestateconstant forall outport unique yconstraint from polymorph uniteconvert hide position unwrapcopy if pragma y usingcreate in print variantcurrentprogram y inport procedure where ydeclare insert process y whilede�nitions inspect real wrapdiscard integer receive others ydissolve into recordTABLE A.1. Hermes keywords. Reserved words are marked with a dagger (y).Reserved words cannot be used as identi�ers.( { various uses ) various uses{ { typestate delimiter } typestate delimiter, { list separator ; statement terminator! { module quali�er : is de�ned. { component selection <- move# { type speci�er | concatenation-> { case declaration := assignment= { equal <> not equal< { less than > greater than<= { less or equal >= greater or equal/ { divide * multiply+ { plus - minusTABLE A.2. Hermes punctuation sequences.

138 A.1. Lexical RulesA.2 Syntactic RulesA module is introduced by a header which names the module and option-ally supplies lists of de�nitions modules to be imported and (for processmodules) other process modules to be statically linked. The module bodyfollows.module ::= module{name : [ imports ] [ linking ]process{module{body::= module{name : [ imports ]de�nitions{module{bodyimports ::= using ( [ module{name [ , module{name ] . . . ] )linking ::= linking ( [ link{name [ , link{name ] . . . ] )A process module body begins with a declaration for the init port, andwhat then follows looks almost like a block body: an optional declarationssection, followed by the statements forming the process body, optionallyfollowed by one or more exception handler speci�cationsprocess{module{body::= process ( declaration )[ pragma ][ declaration{section ]begin[ statement ; ] . . .[ handler ] . . .end processA declaration contains an identi�er (the variable being declared) and thename of its type. A pragma is optional prior to the type name.declaration{section::= declare [ declaration ; ] . . .declaration ::= base{variable : [ pragma ] type{nameStatements come in two basic varieties: simple and compound. Either mayoptionally be preceded by a pragma.statement ::= [ pragma ] simple{statement::= [ pragma ] compound{statement

Appendix A. Hermes Concrete Syntax 139The simple statements:simple{statement::= result{variable source{expression::= result{variable := source{expression::= assert attribute::= call outport{variable call{arguments::= call ( outport{expression ) call{arguments::= checkde�nitions de�nitions{module{variableagainst de�nitions{library{variable::= connect outport{variable to inport{variable::= discard variable{name::= dissolve variant{component into result{variable::= drop attribute::= exit exit{name::= extract table{variable from selector::= hide variant{variable::= insert element{expression into table{variable[ at position{expression ]::= merge table{expression into table{variable[ at position{expression ]::= new variable{name::= print expression::= receive callmessage{variable from inport{variable::= remove element{variable from selector::= return callmessage{variable[ exception user{exception{name ]::= reveal variant{component::= send source{expression to outport{expression::= unite variant{component from source{expression::= unwrap result{variablefrom polymorph{expression unwrapped{typestate::= wrap source{expression as polymorph{variableThe compound statements:

140 A.2. Syntactic Rulescompound{statement::= block[ constant{section ][ declaration{section ]begin[ statement ; ] . . .[ handler ] . . .end block::= for selector inspect[ statement ; ] . . .end for::= for enumeration{variable : enumeration{type repeat[ statement ; ] . . .end for::= if test{expressionthen [ statement ; ] . . .[ else [ statement ; ] . . . ]end if::= inspect selector begin[ statement ; ] . . .end inspect::= inspect declarationfrom polymorph{expression inspect{typestatebegin[ statement ; ] . . .end inspect::= select [ select{expression ][ select{clause ] . . .otherwise{clauseend select::= while test{expression repeat[ statement ; ] . . .end whileVarious syntactic elements appearing in compound statements:constant{section::= constant ( [ base{variable [ , base{variable ] . . . ] )handler ::= on ( [ exception{name [ , exception{name ] . . . ] )[ statement ; ] . . .::= on exit ( [ exit{name [ , exit{name ] . . . ] )[ statement ; ] . . .select{clause::= boolean{guard [ statement ; ] . . .

Appendix A. Hermes Concrete Syntax 141::= event{guard [ statement ; ] . . .::= event{guard and boolean{guard [ statement ; ] . . .event{guard::= event inport{variableboolean{guard::= where ( test{expression )otherwise{clause::= otherwise [ statement ; ] . . .Three syntaxes are available for selectors (used in table operations): a longform (using where), and two shorthand forms (using square brackets {[. . .]).selector ::= base{variable in table{variablewhere ( selector{expression )::= base{variable in table{variable[ [ expression [ , expression ] . . . ] ]::= table{variable [ [ expression [ , expression ] . . . ] ]The Hermes expression syntax follows. All the Hermes binary operatorsare left-associative, though the grammar shown here does not explicitlyrepresent this.expression ::= disjunctiondisjunction ::= conjunction::= disjunction or conjunctionconjunction::= relation::= conjunction and relationrelation ::= concat::= concat = concat::= concat < concat::= concat > concat::= concat <> concat::= concat <= concat::= concat >= concatconcat ::= term::= concat j term

142 A.2. Syntactic Rulesterm ::= factor::= { factor::= term + factor::= term { factorfactor ::= secondary::= factor * secondary::= factor / secondary::= factor mod secondary::= factor rem secondarysecondary ::= primary::= the{element::= case of secondary::= convert of secondary::= copy of secondary::= create of secondary::= empty of secondary::= evaluate declaration from [ statement ; ] . . . end::= every of selector::= exists of selector::= forall of selector::= not secondary::= position of element{variable::= position of selector::= procedure of secondary::= size of secondary::= type of secondary::= typestate of secondaryprimary ::= type{speci�er typed{primary::= typed{primary::= variable{name::= function{referencetype{speci�er::= type{name #typed{primary::= literal

Appendix A. Hermes Concrete Syntax 143::= ( expression )::= currentprogram::= uniquethe{element::= selectorfunction{reference::= outport{primary call{argumentsLiterals of various types:literal ::= integer{literal::= real{literal::= named{literal::= string{literal::= program{literal::= attributename{literal::= typename{literalprogram{literal::= process ( declaration )[ pragma ][ declaration{section ]begin[ statement ; ] . . .[ handler ] . . .end process::= process link{nameattributename{literal::= n attributename attribute{name ntypename{literal::= n typename type{name nA de�nitions module comprises a collection of type and constraint de�ni-tions. A type de�nition associates a name with a type construction, whilea constraint de�ninition associates a name with an attribute construction.A pragma may optionally appear with either type of de�nition.de�nitions{module{body::= de�nitions[ de�nition ; ] . . .end de�nitions

144 A.2. Syntactic Rulesde�nition ::= de�nition{name : [ pragma ] constructionconstruction::= type{construction::= attribute{constructionType constructions:type{construction::= boolean ( boolean{association )::= callmessage ([ component{declaration [ , component{declaration ]. . . ])[ constant{parameters ]exit exit{typestate[ minimum ][ user{exception ] . . .::= [ ordered ] enumeration ([ named{literal [ , named{literal ] . . . ])::= inport of callmessage{type entry{typestate::= integer::= nominal::= outport of inport{type::= polymorph::= real of accuracy integer{literal / integer{literal::= record ([ component{declaration [ , component{declaration ]. . . ])::= [ ordered ] table of element{type element{typestate[ keys [ key ] . . . ]::= variant of enumeration{type ([ case{declaration [ , case{declaration ] . . . ])Various syntactic elements used in type de�nitions:boolean{association::= true : named{literal , false : named{literal::= false : named{literal , true : named{literal

Appendix A. Hermes Concrete Syntax 145constant{parameters::= constant ( [ component{name [ , component{name ] . . .] )minimum ::= minimum minimum{typestateuser{exception::= exception user{exception{name exception{typestatekey ::= ( [ formal{variable [ , formal{variable ] . . . ] )case{declaration::= named{literal ! component{declarationcomponent{typestatecomponent{declaration::= declarationAttribute constructions:attribute{construction::= constraint ([ declaration [ , declaration ] . . . ])is constraint{typestate constraint{expressionTypestate speci�cations appear in attribute constructions. A typestatespeci�cation consists of a collection of attributes, each of which includes anattribute name and a list of arguments. Formal typestates appear in theunwrap and for...inspect statements, and in several type constructions(e.g. variants, tables, inports). In a formal typestate speci�cation, each at-tribute argument is taken to be a component of an assumed base variablewhich depends on the context.typestate ::= f [ attribute [ , attribute ] . . . ] gattribute ::= attribute{name attribute{argumentsformal{typestate::= f [ formal{attribute [ , formal{attribute ] . . . ] gformal{attribute::= attribute{name formal{attribute{argumentsArgument lists appear in various contexts, including call statements andtypestate attributes. They match actual arguments to parameters. Posi-tional matching is achieved with a comma-separated list of arguments. Akeyword-based arguments list explicitly includes the parameter names sothat position is unimportant.The various types of argument lists are distinguished only by what isallowed as an argument.

146 A.2. Syntactic Rulescall{arguments::= ( [ expression [ , expression ] . . . ] )::= ( labeled{expression [ , labeled{expression ] . . . )labeled{expression::= identi�er : expressionattribute{arguments::= ( [ variable{name [ , variable{name ] . . . ] )::= ( labeled{variable{name [ , labeled{variable{name ] . . . )labeled{variable{name::= identi�er : variable{nameformal{attribute{arguments::= empty::= ( [ formal{variable [ , formal{variable ] . . . ] )::= ( labeled{formal{variable [ , labeled{formal{variable ] . . .)labeled{formal{variable::= identi�er : formal{variableNames:attribute{name::= de�nition{name::= module{name ! de�nition{namebase{variable::= identi�erbuiltin{exception{name::= identi�ercomponent{name::= identi�erde�nition{name::= identi�erexception{name::= type{name . user{exception{name::= builtin{exception{nameexit{name ::= identi�er

Appendix A. Hermes Concrete Syntax 147formal{variable::= *::= component{name [ . component{name ] . . .link{name ::= identi�ermodule{name::= identi�ertype{name ::= de�nition{name::= module{name ! de�nition{nameuser{exception{name::= identi�ervariable{name::= base{variable [ . component{name ] . . .Variable names appearing in particular contextscallmessage{variable::= variable{namede�nitions{library{variable::= variable{namede�nitions{module{variable::= variable{nameelement{variable::= variable{nameenumeration{variable::= base{variableinport{variable::= variable{nameoutport{variable::= variable{namepolymorph{variable::= variable{nameresult{variable::= variable{nametable{variable::= variable{name

148 A.2. Syntactic Rulesvariant{variable::= variable{namevariant{component::= variable{nameExpressions appearing in particular contexts:constraint{expression::= expressionelement{expression::= expressionoutport{expression::= expressionoutport{primary::= primarypolymorph{expression::= expressionposition{expression::= expressionselect{expression::= expressionselector{expression::= expressionsource{expression::= expressiontable{expression::= expressiontest{expression::= expressionTypes appearing in particular contexts:callmessage{type::= type{nameelement{type::= type{nameenumeration{type::= type{nameinport{type ::= type{name

Appendix A. Hermes Concrete Syntax 149Typestates appearing in particular contexts:component{typestate::= formal{typestateconstraint{typestate::= typestateelement{typestate::= formal{typestateentry{typestate::= formal{typestateexception{typestate::= formal{typestateexit{typestate::= formal{typestateinspect{typestate::= formal{typestateminimum{typestate::= formal{typestateunwrapped{typestate::= formal{typestateVarious odds and ends. Note that the pragma syntax is currently veryopen-ended. It may become more structured in the future.pragma ::= pragma string{literalempty ::=

Appendix BHermes OperationsThis chapter lists all the hermes operations and provides precise rules fortype checking, type inferencing, and typestate checking each operation.B.1 Operation DescriptionsA typical operation description appears in Figure B.1. The example will beused throughout this section.merge{at(destination, source, position) Exceptions: Depletion,RangeError,(DuplicateKey)Type Rules:destination 2 orderedtablesource 2 tableposition predefined!integerdestination� sourcePreconditions:init(source)init(destination)init(position)var(source)var(destination)duplicatekey?(destination) Postconditions:makeuninit(source)killconstraints(destination)Description: Remove all table elements from source and insert theminto destination so that the resulting position of the �rst transferredelement, if any, will be equal to position. All other transferred elementswill follow consecutively, in the same order as they appeared in source.All elements of destination that formerly occupied positions at positionor beyond are shifted so that they appear in the same relative order,and following the last transferred element.Quali�er : absent See x11.6, p. 112FIGURE B.1. Sample operation description

Appendix B. Hermes Operations 151B.1.1 Description HeaderThe operation name appears �rst, in bold-face, followed by an operand list.Our example describes the merge at operation, which takes three operands:destination, source, and position. Some operations take a variable numberof operands. In these cases, the operand list will appear as an ellipsis.On the far right of the header line is a list of the exceptions that canbe raised by the operation. Our example shows that Depletion, RangeEr-ror, and DuplicateKey may all be raised by the merge at operation. Theparentheses surrounding DuplicateKey indicate that in some cases it canbe statically determined, based on the types of the operands, that themerge at operation cannot raise DuplicateKey. The exact conditions de-termining this are speci�ed later in the operation description. In this case,DuplicateKey can be raised only if the destination is a keyed table.B.1.2 Type RulesBelow the description header are the type rules, which form the basis forboth type checking (ensuring that operands are of the correct type), andtype inferencing (deducing the type of undeclared operands from their con-text). The type rules come in two forms: class rules and inference rules.A class rule speci�es that an operand must be of a type that falls in aspeci�ed class. A class rule can never be used to infer a type, since a typeclass contains many types. The following class rule appears in the merge atdescription: destination 2 orderedtableA list of the type classes and their meanings appears in section B.2.An inference rule constrains an operand to be of a particular type. Thattype may or may not depend on the type of another operand in the oper-ation. An example in which no other operand appears can be found in themerge at operation: position predefined!integerFor an example involving two operands, we have the following from theinsert instruction: element elementtypeof(table)The �rst rule requires that the position operand be of the speci�c type,predefined!integer. This type of inference rule is sometimes called an\assignment rule." The second rule requires that element be of the typethat is uniquely determined by applying the elementtypeof \inferencefunction" to the type of the table operand. All inference functions andtheir meanings are listed in section B.3.

152 B.1. Operation DescriptionsAn inference rule can be used for type checking. It can also be used toinfer the type of the left-hand operand, if either the rule is of the assignmentvariety, or if the type of the right-hand operand is already known.A special notation is used for rules involving the sameas inference func-tion, which identi�es two operands that must have the same type. All suchrules in an operation description are displayed as in the following examplefrom the merge at operation:destination� sourceIn this case, the normal asymmetry of inference rules vanishes: the typeof either operand can be inferred from the type of the other.B.1.3 PreconditionsPrecondition and postcondition rules appear in side-by-side boxes belowthe type rules.The precondition rules specify various conditions that must hold at thetime the operation is to be executed. All preconditions must be staticallyveri�able by the compiler.A precondition takes the form of a precondition function followed by alist of operands. An example from the merge at operation is:init(source)This states that the source operand must be initialized before the merge atoperation can take place.Some \preconditions" do not check for any typestate attributes. Theyde�ne conditions under which an exception is known to be possible. For ex-ample, the following precondition indicates that if the destination operandis a keyed table, then the operation can raise DuplicateKey:duplicatekey?(destination)All the precondition functions and their meanings are listed in sectionB.4.B.1.4 PostconditionsThe postcondition rules characterize the e�ects of a normal (non-exception)execution of the operation. Each rule is speci�ed via a postcondition func-tion followed by a list of operands. An example from the merge at operationis: makeuninit(source)

Appendix B. Hermes Operations 153This indicates that following a successful merge at operation, the sourceoperand will no longer be initialized.The postcondition functions and their meanings are listed in section B.5.B.1.5 Special RulesAbsent from the merge at example but present in some operator descrip-tions is a box labeled \Special Rules". This full-width box appears im-mediately following the typestate pre- and post-condition boxes, and liststype and typestate rules that could not be encoded in the rule tables. Thistreatment is mostly required when a rule cannot be expressed solely interms of named operands. Thus, rules regarding data encoded in the state-ment quali�er (described below) appear as special rules. Also, rules aboutthe operands to an operation without a �xed operand list (like call orassert) are speci�ed in this manner.B.1.6 Operation SemanticsFollowing the post- and pre-condition boxes is a brief description of thesemantic meaning of the operation. Below this, at the very bottom of thepanel, are two boxes showing the statement quali�er type for this operation,and a pointer to the section and page in the reference manual that describesthe operation in detail.A statement quali�er contains all parameters of an operation other thanits operands. For example, since a process begins with all its variablesuninitialized (except its initialization port), it would be impossible to setan integer variable to the value \1" without having a special \set-to-one"operator or using a mechanism such as quali�ers. In Hermes, this particularsituation is handled by the integer literal operation, which requires thatan integer value be encoded in the quali�er.The information that appears in quali�ers varies from statement to state-ment, so the quali�er itself is a variant. Its type is predefined!qualifier,and you can �nd its de�nition in appendix C. The quali�er type identi�ed inthe operation description panel identi�es which component of this varianttype applies to this particular operation.B.2 Type ClassesFollowing are the type classes appearing in class rules:� integer� real

154 B.2. Type Classes� boolean� nominal� enumeration� variant� table� polymorph� inport� outport� callmessage� numeric: includes integer and real.� orderedscalar: includes integer, real, and any enumeration de-�ned with the ordered keyword present.� enumerationorboolean: includes enumeration and boolean.� variantcomponent: This is not, strictly speaking, a type class. It isreally a variable class, comprising variables that are components ofvariables whose types are in the variant type class. Thus, x.v is inthe class variantcomponent if and only if x is a variable whose typeis in class variant.� orderedtable: All ordered tables types, i.e., all types de�ned withthe table type constructor and with the ordered keyword present.� string: All ordered table types whose element types are enumera-tions. That is, a type t is in class string if and only if: (1) t is in classorderedtable; and (2) the element type of t is in class enumeration.� newable: All input port, table, and record types, i.e., all types de�nedwith either the inport, table, or record type constructor.� copyable: All types other than input ports, callmessages, and typesthat can contain such objects. This class includes the classes integer,real, nominal, enumeration, boolean, and outport. It also includesthe class polymorph although a polymorph object may wrap an un-copyable object (any attempt to copy such a polymorph would raiseUncopyable). A type in class variant, or a record type (de�ned withthe record type constructor) is in class copyable if and only if thetypes of all the de�ned components are in copyable. A type in classtable is in copyable if and only if the associated element type is incopyable.

Appendix B. Hermes Operations 155B.3 Inference FunctionsFollowing are the inference functions appearing in inference rules. Eachinference function infers a type from its operand. An inference rule relatestwo operands, a source and a target, by specifying that target operand'stype must be the type inferred by the given inference function from thesource operand's type.� sameas(operand): The target and source operands must have thesame type.1� casetypeof(operand): The source type is in class variant. The tar-get type is the enumeration type appearing in the source type's de�-nition.� matchinginportof(operand): The source type is in class outport.The target type is the input port type appearing in the source type'sde�nition.� messagetypeof(operand): The source type is either in class inportor in class outport. In the former case, the target type is the callmes-sage type appearing in the source type's de�nition; in the latter case,if type ip is the input port type appearing in the source type's de�-nition, then the target type is the callmessage type appearing in thede�nition of ip.� elementtypeof(operand): The source type is in class table. Thetarget type is the associated element type appearing in the sourcetype's de�nition.B.4 Precondition FunctionsB.4.1 Typestate PreconditionsThe following preconditions ensure proper typestate prior to an operation.Each precondition can specify typestate attributes that are required in theprogram typestate before invoking the operation, as well as attributes thatmust not be present.In several cases, required attributes are determined by substituting anoperand into a formal typestate that is somehow associated with the opera-tion. This substitution consists of the following: (1) replace any `*' operandto any attribute in the formal typestate with the operation operand; and(2) insert the operation operand, and a dot (`.') before all other attribute1In the operator description panels, we use the special notationop1� op2� . . .� opn to denote all rules opi sameas(opj) for i 6= j.

156 B.4. Precondition Functionsoperands, deriving components of the operation operand. For step (1), re-call that any attribute without operands is treated as an attribute appliedto a single `*' operand.As an example, we substitute x into the formal typestate:finit, case(*, *.a)gThe resulting typestate is:finit(x ), case(x , x.a)g� init(operand): The attribute init(operand) must be present.� full(operand): The attribute init(operand) is required. In addition,if operand is a variant, record, or callmessage, then the preconditionfull(operand.x ) must be (recursively) satis�ed for every componentx contained in operand .� casets(operand , varcomp): The required attributes are found by sub-stituting operand into the case typestate of the variant componentvarcomp (as given in the relevant variant type de�nition). The for-bidden attributes are all other attributes involving operand or itssubcomponents.� initwithoutcase(varcomp):The attribute init(variant) is required,where variant is the variant variable of which varcomp is a compo-nent. All other attributes (notably, casets) involving variant or itssubcomponents are forbidden.� checked(operand): The attributes init(operand) and checked(operand)are required. There are no forbidden attributes.� lowestelementstate(element , table): The required attributes arefound by substituting element into the element typestate provided inthe de�nition of table's type. The forbidden attributes are all otherattributes involving element or its subcomponents.� callpreconditions(): This precondition appears only with the calloperation. The following nonstandard substitution procedure is used:Let ip be the input port type associated with the output port be-ing called, and let cm be the callmessage type associated with ip.The type de�nition for ip includes a formal typestate in which thecomponents of cm appear as arguments. Substitute, for each suchcomponent name in this formal typestate, the corresponding actualargument in the call operation. The resulting typestate comprises theattributes required by this precondition. If there are non-constant pa-rameters, then all attributes involving any of the corresponding actualparameters or their subcomponents, except required components asdetermined above, are forbidden.

Appendix B. Hermes Operations 157� lowestentrycondition(message, port): This precondition appearsonly in the send operation. Let ip be the input port type associatedwith the port operand, which is an output port. Substitute messageinto the formal typestate appearing in ip's type de�nition to yieldthe required attributes. All other attributes involving message or itssubcomponents.� lowestpostcondition(message): This precondition appears only withthe return and return-exception operations. In the former case,the normal exit typestate appearing in the callmessage type de�ni-tion for message determines the preconditions; in the latter case, thetypestate associated with the exception being returned determinesthe preconditions. In either case, message is substituted into the for-mal typestate in to yield the required attributes. All other attributesinvolving message or its components are forbidden.� polymorphprecondition(operand): This precondition appears onlywith the wrap operation. If a formal typestate is speci�ed in theoperation, then the precondition require the attributes obtained bysubstituting operand into the formal typestate and forbid all otherattributes involving operand or its subcomponents. If no formal type-state is given, this precondition has no e�ect.2� assertable(): This precondition appears only with the assert op-eration. If the constraint being asserted is already present, then thisprecondition has no e�ect. Otherwise, substitute actual argumentsfor formal parameters in the typestate appearing with the constraintde�nition to yield the required attributes. There are no forbiddenattributes.B.4.2 Context PreconditionsThe following precondition functions test context-dependent properties ofvariables other than typestate.� var(operand): The value of operand value may change as a result ofthe operation. It must not be constant in the current scope (e.g. viathe constant list in a callmessage de�nition, or in the body of aninspect or for . . . inspect statement).� pos(operand): operand must be the selector variable in an \active"selector involving an ordered table. An active selector is one whosescope includes the current operation, or is associated with an inspect2Note that it is impossible, with the current syntax, to specify a formal type-state in a wrap statement, so this precondition never has any e�ect.

158 B.4. Precondition Functionsor for . . . inspect statement whose body includes the current oper-ation.B.4.3 Conditional ExceptionsThe following preconditions test whether the operation is capable of raisingcertain exceptions in addition to its standard set.� rangeerror?(source, result): This precondition appears only withthe convert operation. If the domain of result does not include theconversion of every value in the domain of source, then a RangeErrorexception is possible.� duplicatekey?(table): If the table has keys, then a DuplicateKeyexception is possible.� uncopyable?(operand): A type is potentially uncopyable if it is aninput port or callmessage type (which are actually uncopyable), apolymorph type, a record or variant type with potentially uncopy-able component types, or a table type with a potentially uncopyableelement type. If operand is of a potentially uncopyable type, then theUncopyable exception is possible.B.5 Postcondition FunctionsThe following functions determine how the typestate changes on normalcompletion of an operation. Each function can cause attributes to be addedand/or dropped.� makeinit(operand): Add the attribute init(operand).� makefull(operand): Add the attribute init(operand). In addition,if operand is a record or callmessage, make all its components recur-sively full.� makeuninit(operand): Drop all attributes involving operand or itssubcomponents.� movets(source, destination): For each attribute involving source or asubcomponent: (1) drop the attribute, (2) substitute destination forsource, and add the substituted attribute if not already present. Dropall other attributes involving destination variable or a subcomponent.Drop all constraint attributes of variables of which destination is asubcomponent.

Appendix B. Hermes Operations 159� copy(source, destination): For each attribute involving source or asubcomponent, substitute destination for source and add the result-ing attribute. Drop any other attribute involving destination or itssubcomponents. Drop all constraint attributes of variables of whichdestination is a subcomponent.� makecase(varcomp): Add the attribute case(variant , varcomp), wherevariant is the variable of which varcomp is a component.� dropcomponents(variant): Drop any case attribute involving vari-ant , and all attributes involving a component of variant .� killvariant(varcomp): Drop all attributes involving the variant ofwhich varcomp is a component, as well as all attributes involving itssubcomponents.� makechecked(operand): If operand 's type is predefined!program,add the attribute checked(operand); if operand 's type is predefined!definitions module,add the attribute checkeddefinitions(operand).� moveelementts(table, element): Substitute element into the elementformal typestate associated with the table's type, and add the re-sulting attributes. Drop all other attributes involving element or itssubcomponents. Drop all constraint attributes involving variables ofwhich element is a subcomponent.� moveentryts(port , message): Substitute message into the entry for-mal typestate associated with port 's (input port) type, and add theresulting attributes. Drop all other attributes involving message orits subcomponents. Drop all constraint attributes involving variablesof which message is a subcomponent.� polymorphts(operand): This postcondition appears only with theunwrap operation. Substitute operand into the formal typestate ap-pearing in the unwrap statement, and add the resulting attributes.Drop all other attributes involving operand or its subcomponents.Drop all constraint attributes involving variables of which operand isa subcomponent.� asserted(): This postcondition appears only with the assert oper-ation. Add the attribute named in the assert statement.� killconstraints(operand): Drop all constraint attributes involvingoperand or any variables of which operand is a subcomponent.

160 B.5. Postcondition FunctionsB.6 Operation DescriptionsFollowing are the detailed descriptions of all the Hermes operations:add(result , source1 , source2 ) Exceptions: DepletionType Rules:source1 2 numeric source2 2 numericresult 2 numeric result� source1� source2Preconditions:init(source1 )init(source2 )var(result) Postconditions:makeinit(result)Description: Store the sum of source1 and source2 in result .Quali�er : absent See x11.4, p. 104and(result , source1 , source2 ) Exceptions: DepletionType Rules:source1 2 boolean source2 2 booleanresult 2 boolean result� source1� source2Preconditions:init(source1 )init(source2 )var(result) Postconditions:makeinit(result)Description: If both source1 and source2 are true, then set result totrue. Otherwise set result to false.Quali�er : absent See x11.4, p. 105assert(. . .) Exceptions: Depletion, ConstraintError, ConstraintFailureType Rules: See Special RulesPreconditions:assertable() Postconditions:asserted()Special Rules: The number of operands must equal the number of param-eters declared in the de�nition of the attribute named in the statementquali�er. Each operand must also match the type of the correspondingattribute parameter.

Appendix B. Hermes Operations 161assert (continued)Description: Evaluate the constraint identi�ed in the instruction quali�erwith the given operands. If the constraint fails, raise ConstraintError.If it raises an exception, raise ConstraintFailure.Quali�er : constraintname See x11.11, p. 132attributename(result) Exceptions: DepletionType Rules:result predefined!attribute--namePreconditions:var(result) Postconditions:makeinit(result)Description: Copy the attribute name object (of type predefined!attribute name)from the instruction quali�er into result .Quali�er : attributename See x11.10, p. 130block() Exceptions: DepletionType Rules: NonePreconditions: None Postconditions: See Special RulesSpecial Rules: The entry typestate to the main clause is identical tothe entry typestate of the block statement. The entry typestate for ahandler clause is the meet of the entry typestates of all exit statementsthat branch to the handler, along with the appropriate exception exittypestates of any statements that can branch to the handler due to anexception. The typestate on normal termination of a block statement isthe meet of the normal exit typestates of all clauses (including the mainclause and all handlers) that can exit normally, minus any attributesinvolving variables declared in the block statement.Description: Execute the instructions in the scope identi�ed by the in-struction quali�er. If an exception or exit occurs that is not handled bya contained block, and if there is an applicable handler listed in thisblock's quali�er, execute the handler. Note that exceptions and exitsare only handled when they occur in the block's main clause (or con-tained subclauses); exceptions or exits occuring within a handler arepassed on to the outer containing block.Quali�er : block See x11.3, p. 96

162 B.6. Operation Descriptionscall(. . .) Exceptions: Depletion, DisconnectedType Rules: See Special RulesPreconditions:callpreconditions()See also Special Rules Postconditions: See Special RulesSpecial Rules: The �rst operand must be in class outport, and its asso-ciated inport type must have a message type in class callmessage. Theremaining operands are the call arguments. The number of argumentsmust equal the number of components in the de�nition of the messagetype, and each argument type must be the same as the correspondingmessage component type. The arguments must not overlap in storage(e.g. a record may not appear in the same argument list as one of itscomponents). The �rst operand must have the init attribute on en-try. Each argument must have a typestate at least as high as the entrytypestate speci�ed for the corresponding parameter in the associatedinport type de�nition; arguments corresponding to non-constant pa-rameters will be coerced down if their typestate is higher than speci�edin the interface. The exit typestate for normal or exceptional returnincludes init of the �rst operand, plus typestates for arguments asspeci�ed in the callmessage type de�nition. Additionally, attributesinvolving only arguments corresponding to constant parameters are re-tained, even if they do not appear in the appropriate exit typestate.Description: The �rst operand must be an output port. Form a callmes-sage by moving all but the �rst operand into successive callmessagecomponents, and then send the callmessage on the output port. Onreturn of the callmessage (including exception returns), move its com-ponents back into the operand variables from which they were taken.Quali�er : absent See x11.8, p. 121case(result , variant) Exceptions: DepletionType Rules:variant 2 variantresult casetypeof(variant)Preconditions:init(variant)var(result) Postconditions:makeinit(result)

Appendix B. Hermes Operations 163case (continued)Description: Copy the enumeration value corresponding to the currentcase of variant into result .Quali�er : absent See x11.7, p. 117checkde�nitions(module, library) Exceptions: Depletion,De�nitionErrorType Rules:module predefined!definitions--modulelibrary predefined!definitions--modulesPreconditions:init(library)full(module)var(module) Postconditions:makechecked(module)Description: Check that the de�nitions contained in module are correctand consistent, both internally and with respect to all the de�nitionsmodules in library .Quali�er : absent See x11.10, p. 130concatenate(result , source1 , source2 ) Exceptions: Depletion,(Uncopyable),(DuplicateKey)Type Rules:source1 2 orderedtable source1 2 copyablesource2 2 orderedtable source2 2 copyableresult 2 orderedtable result� source1� source2Preconditions:init(source1 )init(source2 )var(result)uncopyable?(source1 )uncopyable?(source2 )duplicatekey?(result) Postconditions:makeinit(result)

164 B.6. Operation Descriptionsconcatenate (continued)Description: Create a new table in result that contains copies of all theelements of source1 followed by all the elements of source2 , such thatall the elements from either source table are ordered in result as theywere in the source table.Quali�er : absent See x11.6, p. 109connect(outport , inport) Exceptions: DepletionType Rules:inport 2 inport outport 2 outportinport matchinginportof(outport)Preconditions:init(inport)var(outport) Postconditions:makeinit(outport)Description: Create an output port connected to inport , and store it inoutport .Quali�er : absent See x11.8, p. 121convert(result , source) Exceptions: Depletion, (RangeError)Type Rules:source 2 orderedscalar result 2 orderedscalarPreconditions:init(source)var(result)rangeerror?(source, result) Postconditions:makeinit(result)Description: Convert the value of source to the appropriate correspond-ing value in the domain of result , and store the converted value in resultQuali�er : absent See x11.4, p. 106copy(result , source) Exceptions: Depletion, (Uncopyable)Type Rules:source 2 copyable result 2 copyableresult� source

Appendix B. Hermes Operations 165copy (continued)Preconditions:init(source)var(result)uncopyable?(source) Postconditions:copy(source, result)Description: Make a copy of the object stored in source, and store thecopy in result .Quali�er : absent See x11.1, p. 94create(result , program) Exceptions: Depletion, InterfaceMismatchType Rules:result 2 outportprogram predefined!programPreconditions:checked(program)var(result) Postconditions:makeinit(result)Description: Instantiate program as a process, and store an output portconnected to the process' initialization port in result .Quali�er : absent See x11.8, p. 124currentprogram(result) Exceptions: DepletionType Rules:result predefined!programPreconditions:var(result) Postconditions:makechecked(result)Description: Store a copy of the program object fromwhich the executingprocess was instantiated, into result .Quali�er : absent See x11.10, p. 129discard(variable) Exceptions: |Type Rules: NonePreconditions:var(variable) Postconditions:makeuninit(variable)

166 B.6. Operation Descriptionsdiscard (continued)Description: Discard the object stored in variable, leaving variable unini-tialized.Quali�er : absent See x11.1, p. 95dissolve(result, varcomp) Exceptions: DepletionType Rules:varcomp 2 variantcomponentresult� varcompPreconditions:casets(varcomp, varcomp)var(result)var(varcomp) Postconditions:movets(varcomp, result)killvariant(varcomp)Description: Move the object stored in variant component varcomp intoresult , leaving the variant of which varcomp is a component uninitial-ized.Quali�er : absent See x11.7, p. 116divide(result , source1 , source2 ) Exceptions: Depletion, DivideByZeroType Rules:source1 2 numeric source2 2 numericresult 2 numeric result� source1� source2Preconditions:init(source1 )init(source2 )var(result) Postconditions:makeinit(result)Description: Store the quotient of source1 divided by source2 in result .Quali�er : absent See x11.4, p. 104drop(. . .) Exceptions: |Type Rules: See Special RulesPreconditions: None Postconditions: None

Appendix B. Hermes Operations 167drop (continued)Special Rules: The number of operands must equal the number of param-eters declared in the de�nition of the attribute named in the statementquali�er. Each operand must also match the type of the correspondingattribute parameter.Description: Remove the constraint identi�ed by the instruction quali-�er, applied to the given operands, from the current program typestate.Quali�er : constraintname See x11.11, p. 132empty(result , inport) Exceptions: DepletionType Rules:result 2 boolean inport 2 inportPreconditions:init(inport)var(result) Postconditions:makeinit(result)Description: If there are messages queued for receipt on inport , set resultto false. Otherwise set result to true.Quali�er : absent See x11.8, p. 120equal(result , source1 , source2 ) Exceptions: DepletionType Rules:result 2 boolean source1� source2Preconditions:init(source1 )init(source2 )var(result) Postconditions:makeinit(result)Description: If source1 and source2 are indistinguishable objects, setresult to true. Otherwise set result to false.Quali�er : absent See x11.1, p. 94every(result , table) Exceptions: Depletion, (Uncopyable)Type Rules:result 2 table table 2 tabletable 2 copyable result� tableSee also Special Rules

168 B.6. Operation Descriptionsevery (continued)Preconditions:init(table)var(result)uncopyable?(table)See also Special Rules Postconditions:makeinit(result)See also Special RulesSpecial Rules: The statement quali�er is a selector. The result variableidenti�ed in that selector must be of type predefined!boolean andmust have the init attribute on normal exit from the selector. Thetype of the element variable identi�ed in the selector must be the el-ement type of table. The entry typestate for the selector is the entrytypestate of the every statement. The typestate on normal exit fromthe every statement is computed by applying the postcondition ruleslisted above to the normal exit typestate of the selector, minus any at-tributes involving the result or element variable. See Section ?? for adiscussion of how typestates are computed for selectors.Description: Create a new table containing a copy of every element oftable that satis�es the selector identi�ed in the instruction quali�er,and store the new table in result . If table is ordered, the elements inresult appear in the same relative order as they do in table.Quali�er : selector See x11.6, p. 110exists(result , table) Exceptions: DepletionType Rules:result 2 boolean table 2 tableSee also Special RulesPreconditions:init(table)var(result)See also Special Rules Postconditions:makeinit(result)See also Special Rules

Appendix B. Hermes Operations 169exists (continued)Special Rules: The statement quali�er is a selector. The result variableidenti�ed in that selector must be of type predefined!boolean andmust have the init attribute on normal exit from the selector. Thetype of the element variable identi�ed in the selector must be the el-ement type of table. The entry typestate for the selector is the entrytypestate of the forall statement. The typestate on normal exit fromthe forall statement is computed by applying the postcondition ruleslisted above to the normal exit typestate of the selector, minus any at-tributes involving the result or element variable. See Section ?? for adiscussion of how typestates are computed for selectors.Description: If any element of table satis�es the selector identi�ed inthe instruction quali�er, then set result to true. Otherwise set result tofalse. In particular, if table is empty, set result to false.Quali�er : selector See x11.6, p. 110exit() Exceptions: |Type Rules: NonePreconditions: None Postconditions: See Special RulesSpecial Rules: The exit statement cannot terminate normally and there-fore has no normal termination typestate.Description: Raise an exit condition for the exit identi�ed in the instruc-tion quali�er. Control transfers to appropriate handler in the innermostcontaining block that handles this exit condition.Quali�er : exit See x11.3, p. 101expression{block() Exceptions: DepletionType Rules: NonePreconditions: See Special Rules Postconditions: See Special RulesSpecial Rules: The typestate on entry to the expression clause is identicalto entry typestate for the expression block statement. The result vari-able must have the init attribute on exit from the expression clause.The typestate on normal exit from the expression block statement isthe normal exit typestate of the expression clause, minus any attributesinvolving variables declared in the expression block statement.

170 B.6. Operation Descriptionsexpression{block (continued)Description: Execute the instructions contained in the scope identi�edby the instruction quali�er. Modulo exceptions, the e�ect will be tocompute a value for a result variable also identi�ed in the quali�er.Quali�er : expression block See x11.3, p. 100extract(result , table) Exceptions: DepletionType Rules:result 2 table table 2 tableresult� tableSee also Special RulesPreconditions:var(result)init(table)var(table)See also Special Rules Postconditions:makeinit(result)killconstraints(table)See also Special RulesSpecial Rules: The statement quali�er is a selector. The result variableidenti�ed in that selector must be of type predefined!boolean andmust have the init attribute on normal exit from the selector. Thetype of the element variable identi�ed in the selector must be the el-ement type of table. The entry typestate for the selector is the entrytypestate of the extract statement. The typestate on normal exit fromthe extract statement is computed by applying the postcondition ruleslisted above to the normal exit typestate of the selector, minus any at-tributes involving the result or element variable. See Section ?? for adiscussion of how typestates are computed for selectors.Description: Remove all the elements from table that satisfy the selectoridenti�ed in the instruction quali�er, assemble them into a new table,and store the new table in result . If table is ordered, the elements ofresult will appear in the same relative order as the did in table.Quali�er : selector See x11.6, p. 111forall(result , table) Exceptions: DepletionType Rules:result 2 boolean table 2 tablePreconditions:init(table)var(result) Postconditions:makeinit(result)

Appendix B. Hermes Operations 171forall (continued)Description: If every element of table satis�es the selector identi�ed inthe instruction quali�er, then set result to true. Otherwise set result toempty. In particular, if table is empty, set result to true.Quali�er : selector See x11.6, p. 110for{enumerate() Exceptions: DepletionType Rules: See Special RulesPreconditions: See Special Rules Postconditions: See Special RulesSpecial Rules: The enumerator variable identi�ed in the statement qual-i�er must be in class enumeration. The entry typestate for the bodyclause contains all the attributes in the meet of the entry typestatefor the for enumerate statement and the normal exit typestate of thebody clause, as well as init of the enumerator variable (an interativesolution is required for this, as described in Section ??). The typestateon normal termination of the for enumerate statement contains all theattributes in the normal exit typestate of the body clause, minus anyattributes involving the enumerator variable or any variables declaredin the body.Description: Execute the statements contained in the scope identi�ed inthe instruction quali�er repeatedly. The statements are executed oncefor each enumeration value in the enumeration type corresponding toenumerator variable also identi�ed in the quali�er. During each exe-cution, the enumerator variable is set to the enumeration value corre-sponding to the current iteration. If the enumeration type is ordered,its values are iterated over in ascending order.Quali�er : for enumerate See x11.4, p. 106for{inspect(table) Exceptions: DepletionType Rules:table 2 tableSee also Special RulesPreconditions:init(table)See also Special Rules Postconditions: See Special Rules

172 B.6. Operation Descriptionsfor{inspect (continued)Special Rules: The statement quali�er contains a selector. The resultvariable identi�ed in that selector must be of type predefined!booleanand must have the init attribute on normal exit from the selector. Thetype of the element variable identi�ed in the selector, and that of theelement variable identi�ed direclty in the quali�er, must be the elementtype of table. The entry typestate for the selector is the entry typestateof the for inspect statement. The entry typestate of the body clauseidenti�ed in the quali�er includes init of the element variable identi�edin the quali�er, plus all attributes in the meet of (1) the exit typestateof the selector, minus any attributes involving the selector's result orelement variable; and (2) the exit typestate of the body clause (aniterative solution is required for this, as described in Section ??). Thetypestate on normal termination of the for inspect statement containsall the attributes in the normal exit typestate of the body clause, minusany attributes involving the element variable identi�ed in the quali�eror any variable declared in the body. See Section ?? for a discussion ofhow typestates are computed for selectors.Description: Execute the statements contained in the scope identi�ed bythe instruction quali�er repeatedly. The statements are executed oncefor each element of table that satis�es the selector identi�ed in the qual-i�er. During execution of the body, the inspect variable (also identi�edin the quali�er) is set to a constant copy of the current matching ta-ble element. If table is ordered, matching elements will be inspected inthe order they appear in table. table itself is not held constant duringexecutions of the body.Quali�er : inspect table See x11.6, p. 113greater(result , source1 , source2 ) Exceptions: DepletionType Rules:source1 2 orderedscalar source2 2 orderedscalarresult 2 boolean source1� source2Preconditions:init(source1 )init(source2 )var(result) Postconditions:makeinit(result)

Appendix B. Hermes Operations 173greater (continued)Description: If source1 compares greater than source2 (numerically orvia an enumeration ordering), then set result to true. Otherwise setresult to false.Quali�er : absent See x11.4, p. 105greater{equal(result , source1 , source2 ) Exceptions: DepletionType Rules:source1 2 orderedscalar source2 2 orderedscalarresult 2 boolean source1� source2Preconditions:init(source1 )init(source2 )var(result) Postconditions:makeinit(result)Description: If source1 compares greater than or equal to source2 (nu-merically or via an enumeration ordering), then set result to true. Oth-erwise set result to false.Quali�er : absent See x11.4, p. 105hide(variant) Exceptions: |Type Rules:variant 2 variantPreconditions:init(variant) Postconditions:dropcomponents(variant)Description: Remove typestate attributes as required by the postcondi-tions. Runtime coercions may result, but otherwise there is no runtimee�ect.Quali�er : absent See x11.7, p. 117if() Exceptions: |Type Rules: See Special RulesPreconditions: See Special Rules Postconditions: See Special Rules

174 B.6. Operation Descriptionsif (continued)Special Rules: The test variable identi�ed in the statement quali�er mustbe of type predefined!boolean and must have typestate init on nor-mal exit from the test clause identi�ed in the quali�er. The typestate onentry to the test clause is the entry typestate for the if statement. Thetypestate on entry to the then clause and (if present) the else clauseis identical to the exit typestate from the test clause. The typestateon normal exit from the if statement is the meet of the normal exittypestates of the then and else clauses.Description: Execute the statements in the test clause identi�ed in theinstruction quali�er. If the resulting value in the test variable (alsoidenti�ed in the quali�er) is true, then execute the statements in the\then" clause (also identi�ed in the quali�er). Otherwise, if an \else"clause is identi�ed in the quali�er, execute the statements in that clause.Quali�er : if See x11.3, p. 97insert(table, element) Exceptions: Depletion, (DuplicateKey)Type Rules:table 2 tableelement elementtypeof(table)Preconditions:init(table)lowestelementstate(element , table)var(table)var(element)duplicatekey?(table) Postconditions:makeuninit(element)killconstraints(table)Description: Insert the object stored in element into table, leaving ele-ment uninitialized. If table is ordered, the new element will follow allpreviously existing elements.Quali�er : absent See x11.6, p. 110

Appendix B. Hermes Operations 175insert{at(table, element , position) Exceptions: Depletion,RangeError,(DuplicateKey)Type Rules:table 2 orderedtableposition predefined!integerelement elementtypeof(table)Preconditions:init(table)lowestelementstate(element , table)init(position)var(table)var(element)duplicatekey?(table) Postconditions:makeuninit(element)killconstraints(table)Description: Insert the object stored in element into table, so as to makethe position of the newly inserted element in the resulting table equalto position, and leaving element uninitialized. All elements that previ-ously occupied element positions at or beyond position are shifted oneposition higher in the table.Quali�er : absent See x11.6, p. 110inspect{polymorph(polymorph) Exceptions: Depletion,PolymorphMismatchType Rules:polymorph 2 polymorphPreconditions:init(polymorph) Postconditions: See Special RulesSpecial Rules: The entry typestate for the body clause identi�ed in thestatement quali�er is the entry typestate for the inspect polymorphstatement, plus all attributes resulting from subsitution of the elementvariable identi�ed in the quali�er into the formal typestate appearing inthe quali�er. The exit typestate for the inspect polymorph statementis the exit typestate of the body clause, minus any attributes involvingthe element variable or any variables declared in the body.

176 B.6. Operation Descriptionsinspect{polymorph (continued)Description: Execute the statements contained in the scope identi�ed inthe instruction quali�er. During execution, the inspect variable (alsoidenti�ed in the quali�er) is set to the value wrapped in polymorph andis held constant. polymorph itself is not held constant.Quali�er : inspect polymorph See x11.9, p. 127inspect{table(table) Exceptions: Depletion, NotFoundType Rules:table 2 tableSee also Special RulesPreconditions:init(table)See also Special Rules Postconditions: See Special RulesSpecial Rules: The statement quali�er contains a selector. The resultvariable identi�ed in that selector must be of type predefined!booleanand must have the init attribute on nomral exit from the selector. Thetype of the element variable identi�ed in the selector, and that of theelement variable identi�ed directly in the quali�er, must be the elementtype of table. The entry typestate for the selector is the entry typestateof the inspect table statement. The entry typestate of the body clauseidenti�ed in the quali�er is the exit typestate of the selector, minus anyattribute involving the selector's result or element variable, plus initof the element variable identi�ed directly in the quali�er. The typestateon normal termination of the inspect table statement contains all theattributes in the normal exit typestate of the body clause, minus anyattributes involving the element variable identi�ed in the quali�er orany variables declared in the body. See Section ?? for a discussion ofhow typestates are computed for selectors.Description: Execute the statements contained in the scope identi�edby the instruction quali�er. During execution of the body, the inspectvariable (also identi�ed in the quali�er) is set to a contstant copy ofa table element that satis�es the selector identi�ed in the quali�er. Iftable is ordered, the �rst table element that satis�es the selector willbe inspected. table itself is not held constant during execution of thebody.Quali�er : inspect table See x11.6, p. 112

Appendix B. Hermes Operations 177integer{literal(result) Exceptions: DepletionType Rules:result 2 integerPreconditions:var(result) Postconditions:makeinit(result)Description: Interpret the digit string stored in the instruction quali�eras a decimal integer, and store value in result .Quali�er : literal See x11.4, p. 104less(result , source1 , source2 ) Exceptions: DepletionType Rules:source1 2 orderedscalar source2 2 orderedscalarresult 2 boolean source1� source2Preconditions:init(source1 )init(source2 )var(result) Postconditions:makeinit(result)Description: If source1 compares less than source2 (numerically or viaan enumeration ordering), then set result to true. Otherwise set resultto false.Quali�er : absent See x11.4, p. 105less{equal(result , source1 , source2 ) Exceptions: DepletionType Rules:source1 2 orderedscalar source2 2 orderedscalarresult 2 boolean source1� source2Preconditions:init(source1 )init(source2 )var(result) Postconditions:makeinit(result)Description: If source1 compares less than or equal to source2 (numeri-cally or via an enumeration ordering), then set result to true. Otherwiseset result to false.Quali�er : absent See x11.4, p. 105

178 B.6. Operation Descriptionsmerge(destination, source) Exceptions: Depletion, (DuplicateKey)Type Rules:destination 2 table source 2 tabledestination� sourcePreconditions:init(source)init(destination)var(source)var(destination)duplicatekey?(destination) Postconditions:makeuninit(source)killconstraints(destination)Description: Remove all the table elements from source and insert theminto destination, leaving source uninitialized. If the tables are ordered,then the transferred elements will appear in destination in the samerelative order as they appeared in source, and following all previouslyexisting elements of destination.Quali�er : absent See x11.6, p. 112merge{at(destination, source, position) Exceptions: Depletion,RangeError,(DuplicateKey)Type Rules:destination 2 orderedtablesource 2 tableposition predefined!integerdestination� sourcePreconditions:init(source)init(destination)init(position)var(source)var(destination)duplicatekey?(destination) Postconditions:makeuninit(source)killconstraints(destination)

Appendix B. Hermes Operations 179merge{at (continued)Description: Remove all table elements from source and insert theminto destination so that the resulting position of the �rst transferredelement, if any, will be equal to position. All other transferred elementswill follow consecutively, in the same order as they appeared in source.All elements of destination that formerly occupied positions at positionor beyond are shifted so that they appear in the same relative order,and following the last transferred element.Quali�er : absent See x11.6, p. 112mod(result , source1 , source2 ) Exceptions: Depletion, DivideByZeroType Rules:source1 2 numeric source2 2 numericresult 2 numeric result� source1� source2Preconditions:init(source1 )init(source2 )var(result) Postconditions:makeinit(result)Description: Let a be the value of source1 , and b be the value of source2 .The value c having the same sign as b, with absolute value less thanthat of b, and satisfying the equation a = n � b + c for some integer n,is stored in result .Quali�er : absent See x11.4, p. 104move(result , source) Exceptions: DepletionType Rules:result� sourcePreconditions:init(source)var(source)var(result) Postconditions:movets(source, result)Description: Move the object stored in source to result , leaving sourceuninitialized.Quali�er : absent See x11.1, p. 93

180 B.6. Operation Descriptionsmultiply(result , source1 , source2 ) Exceptions: DepletionType Rules:source1 2 numeric source2 2 numericresult 2 numeric result� source1� source2Preconditions:init(source1 )init(source2 )var(result) Postconditions:makeinit(result)Description: Store the product of source1 and source2 in result .Quali�er : absent See x11.4, p. 104named{literal(result) Exceptions: DepletionType Rules:result 2 enumerationorbooleanSee also Special RulesPreconditions:var(result) Postconditions:makeinit(result)Special Rules: The character string appearing in the statement quali�ermust equal one of the names appearing in the de�nition of the type ofresult .Description: Set result to the enumeration value belonging to its enu-meration type and named by the instruction quali�er.Quali�er : literal See x11.4, p. 104new(result) Exceptions: DepletionType Rules:result 2 newablePreconditions:var(result) Postconditions:makeinit(result)Description: Create a new object of the appropriate type and store itin result . All components of a newly created record are uninitialized.A newly created table is empty. A newly created input port is notconnected to any output port and has no queued messages.Quali�er : absent See x11.5, p. 107; x11.6, p. 108; and x11.8, p. 120

Appendix B. Hermes Operations 181not(result , source) Exceptions: DepletionType Rules:source 2 boolean result 2 booleanresult� sourcePreconditions:init(source)var(result) Postconditions:makeinit(result)Description: If source is true, set result to false. Otherwise set result totrue.Quali�er : absent See x11.4, p. 105not{equal(result , source1 , source2 ) Exceptions: DepletionType Rules:result 2 boolean source1� source2Preconditions:init(source1 )init(source2 )var(result) Postconditions:makeinit(result)Description: If source1 and source2 are indistinguishable objects, thenset result to false. Otherwise set result to true.Quali�er : absent See x11.1, p. 94or(result , source1 , source2 ) Exceptions: DepletionType Rules:source1 2 boolean source2 2 booleanresult 2 boolean result� source1� source2Preconditions:init(source1 )init(source2 )var(result) Postconditions:makeinit(result)Description: If either source1 is true or source2 is true (or both are true),then set result to true. Otherwise set result to false.Quali�er : absent See x11.4, p. 105

182 B.6. Operation Descriptionsposition{of{element(result , element) Exceptions: DepletionType Rules:result predefined!integerPreconditions:pos(element)var(result) Postconditions:makeinit(result)Description: Set result to the position occupied by element in the ta-ble on which the corresponding selector is operating. If the table haschanged since the selector operation commenced, then the position ofelement at the time the selector operation began is used, rather thanits current position.Quali�er : absent See x11.6, p. 114position{of{selector(result , table) Exceptions: Depletion, NotFoundType Rules:table 2 orderedtableresult predefined!integerSee also Special RulesPreconditions:init(table)var(result)See also Special Rules Postconditions:makeinit(result)See also Special RulesSpecial Rules: The statement quali�er is a selector. The result variableidenti�ed in that selector must be of type predefined!boolean andmust have the init attribute on normal exit from the selector. The typeof the element variable identi�ed in the selector must be the elementtype of table. The entry typestate for the selector is the entry typestateof the position of selector statement. The typestate on normal exitfrom the position of selector statement is computed by applyingthe postcondition rules listed above to the normal exit typestate of theselector, minus any attributes involving the result or element variable.See Section ?? for a discussion of how typestates are computed forselectors.Description: Set result to the position of an element from table thatsatis�es the selector identi�ed in the instruction quali�er. If table isordered, use the position of the �rst such element.Quali�er : selector See x11.6, p. 114

Appendix B. Hermes Operations 183print(variable) Exceptions: |Type Rules: NonePreconditions: None Postconditions: NoneDescription: Produce a printed representation of the object stored invariable. This operation, and the corresponding print statement inthe concrete syntax, are provided only for purposes of Hermes compilerdebugging, and are not meant for use by application programs. Theymay become unavailable without notice.Quali�er : absent |procedure(outport , program) Exceptions: Depletion,InterfaceMismatchType Rules:outport 2 outportprogram predefined!programPreconditions:checked(program)var(outport) Postconditions:makeinit(outport)Description: Create a procedure that will repeatedly instantiate programinto processes, and store an output port matching the type of program'sinitialization port in outport . Whenever a message is sent on this outputport, a new process will be instantiated and the message forwarded tothe new process' initialization port.Quali�er : absent See x11.8, p. 124program{literal(program) Exceptions: DepletionType Rules:program predefined!programPreconditions:var(program) Postconditions:makechecked(program)Description: Make a copy of the program object identi�ed in the instruc-tion quali�er and store it in program.Quali�er : program literal See x11.10, p. 128

184 B.6. Operation Descriptionsreal{literal(result) Exceptions: DepletionType Rules:result 2 integerPreconditions:var(result) Postconditions:makeinit(result)Description: Interpret the character string stored in the instruction qual-i�er as a real number, and store the value in result .Quali�er : literal See x11.4, p. 104rem(result , source1 , source2 ) Exceptions: Depletion, DivideByZeroType Rules:source1 2 numeric source2 2 numericresult 2 numeric result� source1� source2Preconditions:init(source1 )init(source2 )var(result) Postconditions:makeinit(result)Description: Let a be the value of source1 , and b be the value of source2 .The value c having the same sign as a, with absolute value less thanthat of b, and satisfying the equation a = n � b + c for some integer n,is stored in result .Quali�er : absent See x11.4, p. 104receive(message, inport) Exceptions: Depletion, DisconnectedType Rules:inport 2 inportmessage messagetypeof(inport)Preconditions:init(inport)var(inport)var(message) Postconditions:moveentryts(inport , message)Description: Dequeue the �rst available message from inport and storeit in message. If no message is available, wait for one to arrive.Quali�er : absent See x11.8, p. 123

Appendix B. Hermes Operations 185remove(element , table) Exceptions: Depletion, NotFoundType Rules:table 2 tableelement elementtypeof(table)See also Special RulesPreconditions:init(table)var(table)var(element)See also Special Rules Postconditions:moveelementts(table, element)killconstraints(table)See also Special RulesSpecial Rules: The statement quali�er is a selector. The result variableidenti�ed in that selector must be of type predefined!boolean andmust have the init attribute on normal exit from the selector. Thetype of the element variable identi�ed in the selector must be the el-ement type of table. The entry typestate for the selector is the entrytypestate of the remove statement. The typestate on normal exit fromthe remove statement is computed by applying the postcondition ruleslisted above to the normal exit typestate of the selector, minus any at-tributes involving the result or element variable. See Section ?? for adiscussion of how typestates are computed for selectors.Description: Remove an element from table that satis�es the selectoridenti�ed in the instruction quali�er, and store it in element . If table isordered, remove the �rst such element.Quali�er : selector See x11.6, p. 111return(callmessage) Exceptions: DepletionType Rules:callmessage 2 callmessagePreconditions:lowestpostcondition(callmessage)var(callmessage) Postconditions:makeuninit(callmessage)Description: Return callmessage to the process that originally sent it,without raising a user exception.Quali�er : absent See x11.8, p. 123

186 B.6. Operation Descriptionsreturn{exception(callmessage) Exceptions: DepletionType Rules:callmessage 2 callmessagePreconditions:lowestpostcondition(callmessage)var(callmessage) Postconditions:makeuninit(callmessage)Description: Return callmessage to the process that originally sent it,raising the user exception identi�ed by the instruction quali�er in thatprocess.Quali�er : return exception See x11.8, p. 123reveal(varcomp) Exceptions: Depletion, CaseErrorType Rules:varcomp 2 variantcomponentPreconditions:initwithoutcase(varcomp) Postconditions:makecase(varcomp)Description: Add typestate attributes as required by the postconditions.There is no runtime e�ect.Quali�er : absent See x11.7, p. 117select() Exceptions: Depletion, DisconnectedType Rules: See Special RulesPreconditions: None Postconditions: None

Appendix B. Hermes Operations 187select (continued)Special Rules: All the variables identi�ed in the event guards associ-ated with the clauses listed in the statement quali�er must be in classinport. If there is an operand, its type must be the same as that of theresult variable identi�ed in each boolean guard associated with a clauselisted in the quali�er. Otherwise, each boolean guard result variablemust be of type predefined!boolean. All variables associated withevent guards must have the init attribute on entry to the select state-ment. All boolean guard result variables must have the init attributeon normal exit from their associated test clauses. The entry typestatefor each boolean guard clause is identical to the entry typestate for theselect statement. The entry typestate for each select clause, includingthe otherwise clause, is the meet of the normal exit typestate from allthe boolean guard test clauses (or the select statement entry types-tate if there are no boolean guards). The normal exit typestate fromthe select statement is the meet of the normal exit typestates of allthe select clauses, including the otherwise clause.Description: The instruction quali�er contains a table of select clauses,each of which speci�es one or both of an event guard and a booleanguard, as well as an associated statement clause. An event guard isan input port variable, while a boolean guard identi�es a variable anda statement clause. The select operation �rst executes all the state-ments in clauses identi�ed by boolean guards, yielding values for all theboolean guard variables. If the select operation has an operand, it istested for equality with each boolean guard variable, and each selectclause for which the test succeeds becomes \enabled." If the select op-eration has no operands, the boolean guard values must be boolean, andselect clauses with true boolean guard values are enabled. In addition,select clauses without boolean guards are enabled. If any select clauseis enabled, one enabled clause with either no event guard, or with anevent guard whose associated input port is nonempty, is selected. Thestatements in the statement clause associated with the selected clauseare then executed. If there are no enabled select clauses, then the state-ments in the \otherwise" clause (identi�ed in the instruction quali�er)are executed.Quali�er : select See x11.3, p. 98send(outport , message) Exceptions: Depletion, DisconnectedType Rules:outport 2 outportmessage messagetypeof(outport)

188 B.6. Operation Descriptionssend (continued)Preconditions:lowestentrycondition(message, outport)init(outport)var(message) Postconditions:makeuninit(message)Description: Add message to the queue associated with the input portconnected to outport , leaving message uninitialized.Quali�er : absent See x11.8, p. 124size(result , table) Exceptions: DepletionType Rules:table 2 tableresult predefined!integerPreconditions:init(table)var(result) Postconditions:makeinit(result)Description: Set result equal to the number of elements in table.Quali�er : absent See x11.6, p. 114string{literal(result) Exceptions: Depletion, (DuplicateKey)Type Rules:result 2 stringSee also Special RulesPreconditions:var(result)duplicatekey?(result) Postconditions:makeinit(result)Special Rules: Each element of the table appearing in the statementquali�er must correspond to one of the enumeration values appearingin the de�nition of the element type of result . That is, a singleton tablecontaining the element must equal one of the enumeration values.Description: Copy the string stored in the instruction quali�er and storethe copy in result .Quali�er : literal See x11.6, p. 109

Appendix B. Hermes Operations 189subtract(result , source1 , source2 ) Exceptions: DepletionType Rules:source1 2 numeric source2 2 numericresult 2 numeric result� source1� source2Preconditions:init(source1 )init(source2 )var(result) Postconditions:makeinit(result)Description: Subtract source2 from source1 and store the result in result .Quali�er : absent See x11.4, p. 104the{element(result, table) Exceptions: Depletion,NotFound, (Uncopyable)Type Rules:table 2 table table 2 copyableresult elementtypeof(table)See also Special RulesPreconditions:init(table)var(result)uncopyable?(table)See also Special Rules Postconditions:moveelementts(table, result)See also Special RulesSpecial Rules: The statement quali�er is a selector. The result variableidenti�ed in that selector must be of type predefined!boolean andmust have the init attribute on normal exit from the selector. Thetype of the element variable identi�ed in the selector must be the el-ement type of table. The entry typestate for the selector is the entrytypestate of the the element statement. The typestate on normal exitfrom the the element statement is computed by applying the post-condition rules listed above to the normal exit typestate of the selector,minus any attributes involving the result and element variable. See Sec-tion ?? for a discussion of how typestates are computed for selectors.Description: Copy an element from table that satis�es the selector iden-ti�ed in the instruction quali�er, and store the copy in result .Quali�er : selector See x11.6, p. 109

190 B.6. Operation Descriptionstype(result , polymorph) Exceptions: DepletionType Rules:polymorph 2 polymorphresult predefined!typeof--valuePreconditions:init(polymorph)var(result) Postconditions:makefull(result)Description: Store the type of the object wrapped inside polymorph inresult , along with any de�nition modules necessary to resolve the type.Quali�er : absent See x11.9, p. 127typename(result) Exceptions: DepletionType Rules:result predefined!typenamePreconditions:var(result) Postconditions:makefull(result)Description: Make a copy of the typename found in the instruction qual-i�er, and store it in result .Quali�er : typename See x11.10, p. 130typestate(result , polymorph) Exceptions: DepletionType Rules:polymorph 2 polymorphresult predefined!typestateof--valuePreconditions:init(polymorph)var(result) Postconditions:makeinit(result)Description: Store the typestate of the object wrapped inside polymorphin result , along with any de�nition modules necessary to resolve con-straint attributes.Quali�er : absent See x11.9, p. 127

Appendix B. Hermes Operations 191unary{minus(result , source) Exceptions: DepletionType Rules:result 2 numeric source 2 numericresult� sourcePreconditions:init(source)var(result) Postconditions:makeinit(result)Description: Negate source and store the result in result .Quali�er : absent See x11.4, p. 104unique(result) Exceptions: DepletionType Rules:result 2 nominalPreconditions:var(result) Postconditions:makeinit(result)Description: Create a new nominal value, distinguishable from all othernominal values that have ever been created, and store it in result .Quali�er : absent See x11.4, p. 105unite(varcomp, source) Exceptions: DepletionType Rules:varcomp 2 variantcomponentvarcomp� sourcePreconditions:casets(source, varcomp)var(varcomp)var(source) Postconditions:movets(source, varcomp)makecase(varcomp)Description: Put the variant of which varcomp is a component into what-ever state corresponds to varcomp, and then move the value stored insource to varcomp, leaving source uninitialized.Quali�er : absent See x11.7, p. 116

192 B.6. Operation Descriptionsunwrap(result , polymorph) Exceptions: Depletion,PolymorphMismatchType Rules:polymorph 2 polymorphPreconditions:init(polymorph)var(result) Postconditions:polymorphts(result)makeuninit(polymorph)Description: Move the value wrapped inside polymorph to result , leavingpolymorph uninitialized and coercing result as required to lower itstypestate to that given by the instruction quali�er.Quali�er : wrap See x11.9, p. 126while() Exceptions: |Type Rules: NonePreconditions: None Postconditions: See Special RulesSpecial Rules: The result variable identi�ed in the statement quali�ermust be of type predefined!boolean and must have the init attributeon normal exit from the test clause identi�ed in the quali�er. The type-state on entry to the test clause is the meet of the entry typestate ofthe while statement and the typestate on normal termination of therepeated clause. The typestate on entry to the repeated clause, and thenormal exit typestate for the while statement, are identical to the nor-mal exit typestate of the test clause (an iterative solution is requiredfor this, as described in Section ??).Description: Repeatedly execute the statements in the clause identi�edin the instruction quali�er. Prior to each iteration, execute the state-ments in the test clause (also identi�ed in the quali�er) to yield a valuefor the test result variable (identi�ed in the quali�er). If the test resultis ever false, terminate execution of the while operation immediately(without performing the current iteration).Quali�er : while See x11.3, p. 97wrap(polymorph, source) Exceptions: DepletionType Rules:polymorph 2 polymorph

Appendix B. Hermes Operations 193wrap (continued)Preconditions:polymorphprecondition(source)var(polymorph)var(source) Postconditions:makeuninit(source)makeinit(polymorph)Description: Coerce source as necessary to lower its typestate to thatgiven in the instruction quali�er. Then wrap it up with its type andtypestate and store the result in polymorph, leaving source uninitialized.Quali�er : wrap See x11.9, p. 125

Appendix CPrede�ned ModuleThis appendix provides a complete listing of the predefined.d de�nitionsmodule. The predefined module is implicitly imported by all Hermesmodules. It contains all the type de�nitions required by the compiler toenforce assignment-style type rules. For example, the operation descriptionfor the insert-at operation requires that the position argument be of typepredefined!integer. Thus there must be a type named integer de�nedin a de�nitions module named predefined in order for the compiler tofunction. Many types are de�ned in predefined because the module mustbe completely self-contained. For example, type program is included be-cause it is referenced in the type rules for create and procedure. Sincethe de�nition of programmakes use of predefined!processid, a de�nitionfor the latter is also included.It should be stressed that the predefined module is not intended toprovide a collection of type de�nitions that people will �nd generally usefulin their programming. The only criterion by which a type de�nition ischosen for inclusion is the one presented above. It is not uncommon towrite a Hermes application that makes no explicit use of anything frompredefined.The vast majority of the module centers around the type predefined!program,which is used to represent abstract Hermes programs. Any program thatintends to manipulate Hermes program objects as data will necessarily de-pend heavily on the de�nitions in predefined.

Appendix C. Prede�ned Module 195predefined: using ()definitions Fundamental Typesempty: enumeration ();option: enumeration ( 'present', 'absent' );integer: integer;boolean: boolean ( true: 'true', false: 'false' );char: ordered enumeration ('NUL','SOH','STX','ETX','EOT','ENQ','ACK','BEL','BS','HT','NL','VT','NP','CR','SO','SI','DLE','DC1','DC2','DC3','DC4','NAK','SYN','ETB','CAN','EM','SUB','ESC','FS','GS','RS','US',' ','!','"','#','$','%','&','''','(',')','*','+',',','-','.','/','0','1','2','3','4','5','6','7','8','9',':',';','<','=','>','?','@','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','[','n',']','^',' ','�','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','f','|','g','~','DEL');charstring: ordered table of char f init g ;The Abstract Program Object

196 Appendix C. Prede�ned Moduleprogram: pragma "program" record (definitions modules: definitions modules,{ { imported modulesmain program: processid, { { the main programprograms: processes { { one or more programs{ { there can be multiple programs because of program literals);definitions modules: table of definitions modulef full /* , checkeddefinitions */ g keys (id);definitions module: record (id: moduleid, { { unique id of moduletype definitions: type definitions, { { type de�nitionsattr definitions: attr definitions { { attribute de�nitions); Names and IDsprocessid: nominal; { { identi�es a process objectclauseid: nominal; { { clause identi�ersrootid: nominal; { { root name identi�erstypeid: nominal; { { type identi�ersmoduleid: nominal; { { module idscopeid: nominal; { { scope identi�ercomponentid: nominal; { { component identi�erexceptionid: nominal; { { exception idstatementid: nominal; { { statement identi�erattributeid: nominal; { { attribute identi�erexitid: nominal; { { id of exit handler{ { The following is to allow, in absprog, variables with undeclared{ { types. For example, temporaries that get introduced when expanding{ { expressions to assignment statements. Their types are not known{ { until the type checking phase which comes after resolution.typename option: enumeration ( 'named', 'unnamed');optional typename: variant of typename option ('unnamed' -> noname: empty f g,

Appendix C. Prede�ned Module 197'named' -> typename: typename f full g);typename: record (moduleid: moduleid, { { time stamp of moduletypeid: typeid { { type id within module);constraintname: record (moduleid: moduleid, { { time stamp of moduleattributeid: attributeid { { attribute id within module);{ { an object (variable) gives (i) the scope where it is declared, (ii){ { its rootid, and (iii) if it is a component, its compoenent id.{ { e.g., if in scope s there is the declaration r: Q, where Q is a{ { record having a component z, then the object r.z gets would have{ { the objectname (s, r, z).rootname: record (scope: scopeid, { { scope of root declarationroot: rootid { { root identi�er used to{ { refer to this object);objectname: record (root: rootname, { { root objectcomponents: component list { { identi�ers of components);component list: ordered table of componentid f full g;objectnames: ordered table of objectname f full g ;Type De�nitionstype definitions: table of type definition f full g keys (id);{ { Each type has a unique (within its de�nitions module) type id, a

198 Appendix C. Prede�ned Module{ { set of typed components (empty except for records, variants and{ { callmessages), a speci�cation consisting of a primitive type and{ { associated information, and a pragma stringtype definition: record (id: typeid,component declarations: component declarations,specification: specification type,prag: charstring);component declarations: ordered table of component declaration f full gkeys (id) ;component declaration: record (id: componentid, { { unique name of componenttype: typename { { declared type);primitive types: ordered enumeration ('nominaltype', 'integertype', 'booleantype', 'enumerationtype', 'realtype','recordtype', 'varianttype', 'tabletype','inporttype', 'outporttype', 'callmessagetype','polymorphtype' );specification type: variant of primitive types('nominaltype'-> nominal info: empty f g,'integertype' -> integer info: empty f g,'booleantype' -> boolean: boolean info f full g,'enumerationtype' -> enumeration: enumeration info f full g,'realtype' -> accuracy info: accuracy info f full g,'recordtype' -> record info: empty f g,'varianttype' -> variant info: variant info f full g,'tabletype' -> table info: table info f full g,'inporttype' -> inport info: inport info f full g,'outporttype' -> outport info: typename f full g,'callmessagetype' -> callmessage info: callmessage info f full g,'polymorphtype' -> polymorph info: empty f g);

Appendix C. Prede�ned Module 199{ { Speci�c information needed to completely specify a type falling in{ { each of the various primitive type categoriesboolean info: record (true name: charstring,false name: charstring);enumeration info: record (ordered: boolean, { { is it an ordered enumeration?values: enumeration values { { components);enumeration values: ordered table of charstring f init g keys (*);accuracy info: record ( { { for real typesaccuracy numerator: integer,accuracy denominator: integer);variant info: record (case type: typename, { { the enumeration tagging the variantcase mapping: partitionset { { maps case tags (from case-type{ { enumeration) to components);partitionset: table of partition info f full g keys (component id);partition info: record (component id: componentid, { { component of variantcase id: integer, { { corresponding enumeration valuecase typestate: formal typestate { { typestate of component);table info: record (ordered table: boolean, { { is the table ordered?keys: keyset, { { all the keyselement type: typename, { { type of each table elementelement typestate: formal typestate { { typestate of each table element);

200 Appendix C. Prede�ned Modulekeyset: ordered table of formal objects f full g keys (*);{ { Each key consists of a set of (sub-)components of the element type.{ { An empty set indicates the entire element objectformal objects: ordered table of component list f full g;inport info: record (message type: typename, { { type of each queued messagemessage typestate: formal typestate { { typestate of each queued message);callmessage info: record (constants: component set, { { which components are held constantnormal: formal typestate, { { exit typestate for normal return{ { following identi�es the Discarded exception for this{ { callmessage. The associated exit typestate is the minimum{ { typestate for the callmessage type.minimum: exceptionid,{ { following associates exit typestates with exceptionsexception specifications: exception specifications);component set: table of componentid f init g keys (*);exception specifications: table of exception f full g keys (exceptionid);exception: record (exceptionid: exceptionid, { { unique name of exceptionpost typestate: formal typestate { { typestate on that outcome); Attribute De�nitionsattr definitions: table of attr definition f full g keys (attributeid);{ { An attribute de�nition includes a unique (within a de�nitions{ { module) identi�er, the code that evaluates the attribute,{ { parameters, and the required entry typestate of the parameters.{ { The parameters exist in the outer scope of the code.

Appendix C. Prede�ned Module 201attr definition: record (attributeid: attributeid, { { unique id of attributeexecution environment: execution environment,{ { expression to evaluateparameters: rootids, { { declared in the outer scopereturnvalue: objectname, { { boolean objectpretypestate: typestate, { { typestate on entryprag: charstring { { pragma);rootids: ordered table of rootid f init g;Process Modulesprocesses: table of proc f full g keys (id);{ { Each process has a unique identi�er. The code is an object of{ { type execution environment, which includes the statements appearing{ { in the code, as well as declarations for all the root objects{ { introduced by included scopes. The initport is always an object in{ { the outermost scope.proc: record (id: processid,executable part: execution environment,{ { dcls + statementsinitport: rootid, { { name of initialization portprag: charstring { { pragma);{ { Following record represents a piece of code, either from a process{ { module or from an attribute de�nition. The code is represented as{ { a collection of nested scopes, with a single main, or outermost{ { scope. Each scope introduces new declarations for root objects,{ { and is associated with a clause. A clause is a list of statements,{ { some of which introduce nested scopes. Execution of the code{ { always begins with the clause associated with the outermost scope.execution environment: record (scopes: scopes,

202 Appendix C. Prede�ned Moduleclauses: clauses,main scope: scopeid);scopes: table of scope f full g keys (id);scope: record (id: scopeid, { { scope identi�erdeclarations: declarations, { { declarations introduced by scopeclause: clauseid { { statements to execute in scope);{ { Each declaration identi�es the declared root object, and{ { optionally associates a type with the object. If no type is{ { supplied in a declaration, the type must be inferrable from{ { context.declarations: table of declaration f full g keys (id);declaration: record (id: rootid, { { id used for referencetypename: optional typename, { { type of objectprag: charstring { { pragma);{ { Each clause has a unique (within an execution environment) id and a{ { list of the statements that make up the clauseclauses: table of clause f full g keys (id);clause: record (id: clauseid, { { identi�er of clausestatements: statements { { statements of clause);{ { Each statement has a unique (within an execution environment){ { identi�er, an operator and operands, and possibly additional data{ { speci�ed in is quali�er. Any information that is needed for the{ { proper execution of the statement and cannot not be made available{ { via accessible program objects at runtime, must be provided in the{ { quali�er. The precise contents of the quali�er depends on the{ { operator.statements: ordered table of statement f full g keys (id);

Appendix C. Prede�ned Module 203statement: record (id: statementid,operator: operator,operands: objectnames,qualifier: qualifier,prag: charstring);{ { Here are all the Hermes operatorsoperator: ordered enumeration ('add', 'and', 'assert', 'attributename', 'block', 'call', 'case','checkdefinitions', 'concatenate', 'connect', 'convert', 'copy','create', 'currentprogram', 'discard', 'dissolve', 'divide', 'drop','empty', 'equal', 'every', 'exists', 'exit', 'expression block','extract', 'for enumerate', 'for inspect', 'forall', 'greater','greater equal', 'hide', 'if', 'insert', 'insert at','inspect polymorph', 'inspect table', 'integer literal', 'less','less equal', 'merge', 'merge at', 'mod', 'move', 'multiply','named literal', 'new', 'not', 'not equal', 'or','position of element', 'position of selector','print', { { included for debugging purposes only'procedure', 'program literal', 'real literal', 'receive', 'rem','remove', 'return', 'return exception', 'reveal', 'select', 'send','size', 'string literal', 'subtract', 'the element', 'type','typename', 'typestate', 'unary minus', 'unique', 'unite', 'unwrap','while', 'wrap'); Operation Quali�ers{ { Names of all the quali�er types... comments identify which{ { operators use each quali�er typequalifier types: ordered enumeration ('absent','attributename', { { attributename literal'block', { { block

204 Appendix C. Prede�ned Module'constraintname', { { assert, drop'exit', { { exit'expression block', { { expression block'for enumerate', { { for enumerate'if', { { if-then-else'inspect polymorph', { { inspect polymorph'inspect table', { { for inspect, inspect table'literal', { { integer literal,{ { real literal, named literal'program literal', { { program literal'return exception', { { return exception'select', { { select'selector', { { every, exists, extract,{ { forall, remove,{ { the element, position of selector'typename', { { typename literal'while', { { while'wrap' { { wrap, unwrap);{ { Following is the variant used to represent all statement quali�ersqualifier: variant of qualifier types('absent' -> empty: empty f g, { { no quali�er'attributename' -> attributename: attribute name f full g,'block' -> block: block qualifier f full g,'constraintname' -> constraintname: constraintname f full g,'exit' -> exit: exitid f full g,'expression block' -> expression: expression qualifier f full g,'for enumerate' -> for enumerate: for enumerate qualifier f full g,'if' -> if: if qualifier f full g,'inspect polymorph' ->inspect polymorph: inspect polymorph qualifier ffullg,'inspect table' -> inspect table: inspect table qualifier f full g,'literal' -> literal: charstring f full g,'program literal' -> program literal: processid f full g,'return exception' -> exceptionid: exceptionid f full g,'select' -> select: select qualifier f full g,'selector' -> selector: selector f full g,'typename' -> typename: typename f full g,'while' -> while: while qualifier f full g,'wrap' -> formal typestate: formal typestate f full g);

Appendix C. Prede�ned Module 205{ { A block statement quali�er identi�es the scope holding the{ { declarations and main body code of the block, the root objects held{ { constant within the block, and all the handlers associated with the{ { block.block qualifier: record (scope: scopeid, { { new scope introduced by the blockconstants: rootnames, { { objects not changed in this scopehandlers: handlers { { exception and exit handlers);rootnames: table of rootname f full g keys (*);{ { Each handler names the exceptions or exit conditions that it{ { handles, and supplies a clause (not a scope) containing the body of{ { the handler.handlers: table of handler f full g keys (id);handler: record (id: handlername,clause: clauseid);{ { Handlers come in four varieties...handler type: ordered enumeration ('builtin', { { builtin exceptions'user', { { user exceptions (de�ned{ { with callmessages)'exit', { { exit conditions'others' { { exceptions not otherwise handled);{ { Each handler type requires type-speci�c additional information to{ { fully specify the condition handledhandlername: variant of handler type ('builtin' -> builtin: builtin exception f init g,'user' -> user: user exception f full g,'exit' -> exit: exitid f init g,'others' -> others: empty fg

206 Appendix C. Prede�ned Module);{ { Here are all the builtin exceptionsbuiltin exception: ordered enumeration ('CaseError', 'ConstraintError', 'ConstraintFailure', 'Depletion','Disconnected', 'DivideByZero', 'DuplicateKey', 'InterfaceMismatch','NotFound', 'PolymorphMismatch', 'DefinitionError', 'RangeError','Uncopyable');{ { A user exception is speci�ed by the callmessage type and one of{ { the exceptions de�ned with that typeuser exception: record (type: typename,exceptionid: exceptionid);{ { The quali�er for an expression block identi�es the scope to be{ { executed and the root variable that will hold the expression{ { result. The root variable is the only variable outside the{ { expression block scope that is not held constant within the scope.expression qualifier: record (scope: scopeid,result: rootname);{ { For a for enumerate statement, the quali�er identi�es the body{ { scope, as well as the root variable that is used to iterate through{ { the enumeration. The iteration variable is declared in the body{ { scope.for enumerate qualifier: record (scope: scopeid,enumerator: rootid);{ { The quali�er for an if statement identi�es the clause that will{ { evaluate the test expression, the object that will hold the{ { expression result, and the clause to be executed if the expression{ { yields a true result. In addition, an 'else' clause may optionally{ { be supplied, identifying the clause to be executed when the test

Appendix C. Prede�ned Module 207{ { expression is false.if qualifier: record (test clause: clauseid,test result: objectname,then clause: clauseid,opt else clause: optional clauseid);optional clauseid: variant of option ('present' -> clauseid: clauseid f init g,'absent' -> empty: empty f g);{ { The quali�er for an inspect polymorph statement identi�es the{ { scope forming the statement body, the root variable (declared in{ { the body scope) that will hold the unwrapped polymorph value{ { throughout the body scope, and the typestate required for the{ { unwrapped value.inspect polymorph qualifier: record (scope: scopeid,element: rootid,typestate: formal typestate);{ { Following quali�er is used for inspect table and for inspect{ { statements. In addition to the same sort of selector as that used{ { for other table operations, a body scope is included, as well as{ { the id of the root object that holds the inspected table element{ { throughout the body scope. The inspecting root object is contained{ { in the body scope. Note that it IS NOT the same object as the{ { element object in the selector itself, as they declared in disjoint{ { scopes.inspect table qualifier: record (scope: scopeid, { { scope of statement bodyelement: rootid, { { inspection variableselector: selector { { table selector);{ { A select statement quali�er includes a "select clause" for each{ { guarded arm of the select statement, identifying the guard{ { condition for the arm and the clause to be executed when the arm

208 Appendix C. Prede�ned Module{ { is selected. In addition, a clause to be executed when all{ { guards are disabled is speci�ed.select qualifier: record (clauses: select clauses,otherwise clause: clauseid { { otherwise clause is mandatory);select clauses: table of select clause f full g ;select clause: record (clause: clauseid,info: clause info);{ { Each arm can be guarded by a boolean condition, an "events" on a{ { given inport, or both.guard type: enumeration ( 'boolean', 'event' ,'both' );clause info: variant of guard type ('boolean' -> boolean: boolguard f full g,'event' -> portname: objectname f full g,'both' -> both: bothguard f full g);{ { A boolean guard (or the boolean part of a "both" guard) identi�es{ { the clause to be executed to evaluate the guard, and the object{ { where the result of that execution will be placed.boolguard: record (clause: clauseid,result: objectname);bothguard: record (boolean: boolguard,portname: objectname);{ { A selector quali�es many table-related statements, and encodes the{ { information contained in the WHERE (...) clause of such statements.{ { The selector introduces a scope which encodes the selector

Appendix C. Prede�ned Module 209{ { expression. The 'element' root variable appears in that scope and{ { holds a table element whenever the selector is executed. The{ { 'result' object holds the result of evaluating the selector for a{ { the table element.selector: record (scope: scopeid, { { scope introduced by the selectorelement: rootid, { { the element being selectedresult: objectname { { boolean value of selector expression);{ { The quali�er for a while statement identi�es the clause that{ { evaluates the entry test at the top of the loop, the variable that{ { will hold the result of the test, and the clause containing the{ { loop body.while qualifier: record (test clause: clauseid,result: objectname,repeated clause: clauseid);{ { Following types de�nitions are for the values resulting from{ { TYPE OF and TYPESTATE OF expressions, which inspect values wrapped{ { in polymorphstypeof value: record (typename: typename, { { name of the typedefinitions: definitions modules { { resolution environment for that type);typestateof value: record (typestate: formal typestate, { { typestate of wrapped valuedefinitions: definitions modules { { resolution environment for{ { attributes appearing in typestate); Typestates and Formal Typestate{ { A typestate is a set of attributes, each of which constist of an

210 Appendix C. Prede�ned Module{ { attribute name and a list of objects that are the parameters of the{ { attribute.typestate: table of attribute f full g keys (*) ;attribute: record (name: attribute name,objects: objectnames);{ { There are three built-in attributes: init and case. An attribute{ { can also name a user-de�ned attribute, or constraint. The 'full'{ { attribute is an abbreviation for several init attributes, and may{ { be dropped from this list at some future time.attribute type: enumeration ( 'initialized', 'case', 'constraint', 'full' );attribute name: variant of attribute type('initialized' -> init: empty f g,'case' -> case: empty f g,'constraint' -> constraint: constraintname f full g,'full' -> full: empty f g);{ { A formal typestate is like a typestate, but no root variable is{ { mentioned. Instead, the component lists are interpreted relative{ { to some variable (not necessarily a root object) depending on{ { context. An empty component list indicates the associated variable{ { itself, rather than any of its (sub-)components.formal typestate: table of formal attribute f full g keys (*) ;formal attribute: record (attribute name: attribute name,parameters: formal object list);formal object list: ordered table of component list f full g ;end definitions

Appendix C. Prede�ned Module 211C.1 References[Ada83] Ada. Reference Manual for the Ada Programming Language.United States Department of Defense, 1983.[ASU88] Alfred V. Aho, Ravi Sethi, and Je�rey D. Ullman. Compilers:Principles, Techniques and Tools. Addison-Wesley, 1988.[AU77] Alfred V. Aho and Je�rey D. Ullman. Principles of CompilerDesign. Addison-Wesley, 1977.[BS89] David F. Bacon and Robert E. Strom. Implementing the hermesprocess model. Technical Report RC 14518, IBM T.J. WatsonResearch Center, 1989.[BS90] David F. Bacon and Robert E. Strom. Optimistic parallelizationof communicating sequential processes. Research Note RC, IBMT.J. Watson Research Center, 1990.[Coo83] Doug Cooper. Standard Pascal User Reference Manual. W. W.Norton, 1983.[FBSY87] Keith Feibusch, David F. Bacon, Robert E. Strom, andShaula Alexander Yemini. Checkpoint management disk inter-face: Optimistic recovery for disk storage. Technical report, IBMT.J. Watson Research Center, 1987.[GJ87] Arthur P. Goldberg and David Je�erson. Transparent processcloning: A tool for load management of distributed programs.In Proceedings of the 1987 International Conference on ParallelProcessing, pages 728{734, 1987.[Jef85] David Je�erson. Virtual time. ACM Transactions on Program-ming Languages and Systems, 7(3):404, July 1985.[KP84] Brian W. Kernighan and Rob Pike. The UNIX ProgrammingEnvironment. Prentice-Hall Software Series, 1984.[KR78] Brian W. Kernighan and Dennis M. Ritchie. The C Program-ming Language. Prentice-Hall Software Series, 1978.[Mor80] William Morris. The American Heritage Dictionary of the En-glish Language. Houghton Mi�in Company, 1980.[NS88] Van L. Nguyen and Robert E. Strom. Process semantics: globalaxioms, compositional rules, and applications. In Proc. SeventhACM Symposium on Principles of Distributed Computing, pages232{247. ACM, August 1988.

212 C.1. References[PS83] Francis N. Parr and Robert E. Strom. NIL: A high-level lan-guage for distributed systems programming. IBM Systems Jour-nal, 22(1-2), 1983.[SBY87] Robert E. Strom, David F. Bacon, and Shaula Alexander Yem-ini. Volatile logging in n-fault-tolerant distributed systems. Re-search Note RC 13373, IBM T.J. Watson Research Center, 1987.[SBY88] Robert E. Strom, David F. Bacon, and Shaula Alexander Yem-ini. Volatile logging in n-fault-tolerant distributed systems.In The Eighteenth Annual International Symposium on Fault-Tolerant Computing: Digest of Papers, pages 44{49, June 1988.[Str86] Robert E. Strom. A comparison of the object-oriented andprocess-oriented paradigms. In IBM/Brown Workshop onObject-Oriented Programming. ACM, SIGPLAN Notices, June1986.[SY83] Robert E. Strom and Shaula Alexander Yemini. NIL: An in-tegrated language and system for distributed programming. InSIGPLAN '83 Symposium on Programming Language Issues inSoftware Systems, June 1983.[SY84] Robert E. Strom and Shaula Alexander Yemini. Optimisticrecovery: An asynchronous approach to fault-tolerance in dis-tributed systems. In The Fourteenth International Symposiumon Fault-Tolerant Computing. IEEE Computer Society, 1984.[SY85] Robert E. Strom and Shaula Alexander Yemini. Optimistic re-covery in distributed systems. ACM Transactions on ComputerSystems, 3(3):204{226, August 1985.[SY86] Robert E. Strom and Shaula Alexander Yemini. Typestate: Aprogramming language concept for enhancing software reliabil-ity. IEEE Transactions on Software Engineering, SE-12(1):157{171, January 1986.[SY87] Robert E. Strom and Shaula Alexander Yemini. Synthesizingdistributed and parallel programs through optimistic transfor-mations. In Yechiam Yemini, editor, Current Advances in Dis-tributed Computing and Communications, pages 234{256. Com-puter Science Press, 1987.[SY89] Robert E. Strom and Daniel M. Yellin. Computationallytractable semilattices for global data ow analysis. TechnicalReport RC 14936, IBM T. J. Watson Research Center, August1989.

Appendix C. Prede�ned Module 213[SY90] Robert E. Strom and Daniel M. Yellin. Extending typestatechecking using conditional liveness analysis. Technical ReportRC 15398, IBM T. J. Watson Research Center, January 1990.[SYB87a] Robert E. Strom, Shaula Alexander Yemini, and David F. Ba-con. A linguistic treatment of programs as typed objects. Re-search Note RC 13325, IBM T.J. Watson Research Center, 1987.[SYB87b] Robert E. Strom, Shaula Alexander Yemini, and David F. Ba-con. Programs as objects. In H. N. Cooper, editor, IBM Pro-gramming Language Technology ITL, pages 187{195. IBM Inter-divisional Technical Liaison, IBM Corporation, February 1987.[SYB87c] Robert E. Strom, Shaula Alexander Yemini, and David F. Ba-con. Toward self-recovering operating systems. In The Interna-tional Conference on Parallel Processing. North-Holland, 1987.[SYB87d] Robert E. Strom, Shaula Alexander Yemini, and David F. Ba-con. Toward self-recovering operating systems. In Harold Lorin,editor, The IBM/SRI Symposium on Operating Systems: Trendsand Issues in Operating Systems, pages 221{233. IBM SystemsResearch Institute, IBM Corporation, 1987.[SYB88] Robert E. Strom, Shaula Alexander Yemini, and David F. Ba-con. A recoverable object store. In Hawaii International Con-ference on System Sciences. IEEE CS, 1988.[SYW85] Robert E. Strom, Shaula A. Yemini, and Peter Wegner. ViewingAda from a process model perspective. In Ada in Use: Proceed-ings of the International Ada Conference, Paris, France, 1985.[YSB87] Shaula Alexander Yemini, Robert E. Strom, and David F. Ba-con. Improving distributed protocols by decoupling recoveryfrom concurrency control. Research Note RC 13326, IBM T.J.Watson Research Center, 1987.

Index--, 73:=, 94<, 105<-, 93<=, 105<>, 94=, 94>, 105>=, 105[ ], 47access control, 53accuracy requirement, 103and, 105approximate arithmetic, 103attribute, 132name resolution, 78attribute arguments, 84, 132attribute compatibility rules, 86attribute not present, 92base variablemultiple declarations of, 76block, 10block, 96{97booleanguard, 26, 98literals, 50type family, 102builtin, 75attribute names, 84exception names, 78typestate attributes, 78call arguments, 22

forwarding, 60interface, 22parameters, 22call, 5, 9, 118, 121arguments and parameters, 119exception names, 78, 119returning typestate, 122callmessage, 8, 22, 94, 118constant components, 88, 119discarded, 29discarding, 95init, 87type, 24callmessage typede�nition, 119cannot add, 92cannot coerce, 92cannot drop, 92capability, see output portcapability-based, 17, 119case, 61case, 78, 84, 86case of, 117case typestate, 86case typestate attribute, 62CaseError exception, 63Charstring, 19, 28checkdefinitions, 131checked, 78, 84, 124checked program, 23checkeddefinitions, 78, 84, 131checking, compile-time, 4child, 8class rule, 151class rules, 55, 80, 81client, 9214

Appendix C. Prede�ned Module 215coercion, 88, 89coercion operations, 57comment, 73comparison, 94, 125of records, 107of variants, 115component nameresolution of, 77components, 107concatenate, 109connect, 14, 121constant, 88constant, 22constant copy, 88, 112, 113, 119,127constraint, 131ConstraintError exception, 132ConstraintFailure exception, 132conversion and resolution, 72convert operator, 106copy of, 29copying a value, 94create, 124create of, 15, 16, 24currentprogram, 129dead code, 91declaration, 18, 19hiding not allowed, 20declarations list, 76de�nition, 18de�nitions module, 21, 77de�nitions modules, 19Depletion exception, 95discard, 57, 95, 95Discarded exception, 29, 119, 123discarded exceptions, 78Disconnected exception, 29, 99,122, 123dissolve, 116DivideByZero exception, 104DuplicateKey exception, 43, 48,111, 112element, 107

element type, 107else, 97empty, 61, 120enabled clause, 99entry typestate, 118enumeration type family, 102evaluate, see expression blockevent guard, 26, 98every, 110every of, 42exception, 11, 119, 122de�nition, 22handler, 11, 96name resolution, 78return, 123exists, 110exists of, 48exit name resolution, 79exit, 79, 88, 123exit, 101exit formal typestate, 119exit typestate, 24expression block, 59, 88, 100{101extract, 42, 111fairnessin receive statement, 122in select statement, 99�lter program, 12for enumerate, 106for statement, 46for-inspect, 113forall, 110formal typestate, 86full, 23, 78, 85functioncall, 121function call, 122function notation, 12generatorprocess, 125generator process, 125guard, 98

216 C.1. Referencesguarded select, 26handler clause, 79, 96Hermes, 3{5research directions, 67type system, 55hide, 117hide statement, 63Hoare, C.A.R., 55identi�ers, 74if, 97imports list, 19, 78inference functionelementtypeof, 151sameas, 152inference rule, 151inference rules, 21, 80inferred declaration, 81init, 23, 78, 84{86init(), 56initialization port, 8, 19inport, 118input port, 8, 22, 94, 118, 119as event guard, 98de�nition, 23, 24enabled, 26insert, 29, 111, 111insert-at, 111inspect, 46, 112inspect variable, 112integer literals, 50integer type family, 103interface, 11, 22, 60de�nition, 22, 24InterfaceMismatch exception, 17,124, 124key, 108, 110{112keys(), 43keyword, 74late binding, 5lexical analysis, 71linking list, 51, 79literal

named, 73numeric, 73real, 73string, 73literals, 104long-lived systems, 55main clause, 96main program, 124meet, 89merge, 112merge-at, 112message type, 118minimum formal typestate, 119default, 119minimum typestate, 87mod, 104module, 4move, 93moving a value, 93moving versus copying, 29, 93name equivalence of type de�ni-tions, 24name space, 75, 78of base variables, 76of component names, 77of type identi�ers, 77of user-de�ned exception names,78named literal, 73new, 14, 107, 119, 120nominal type family, 101, 105normal exit, 96not, 105notationassociation, 121function, 121positional, 121NotFound exception, 42, 110{112numeric literal, 73object-oriented languages, 27occurrenceapplied, 72

Appendix C. Prede�ned Module 217de�ning, 72on (others), 50on(others), 96operating system, 4or, 105orderedtable operations, 29ordered, 108ordered table, 28ordered table, 28others, 96otherwise, 27outcome, 88outport, 119output port, 8, 22, 119de�nition, 24overlap, 121parent, 8passing parameters by value-result,12pointers, prohibited, 4polymorph, 94polymorph, 64polymorph type family, 125polymorphic types, none, 18PolymorphMismatch exception, 66,126, 127position, 41position in a table, 28, 110{112position-of-element, 92position-of-element, 113, 114position-of-selector, 114position-of-selector, 42postcondition, 89postcondition function, 90precondition, 88prede�ned, 19, 75character type, 102predefined, 19, 78prede�ned exceptionsDivideByZero, 104predefined!boolean, 97predefined!program, 51procedure, 40

procedure, 125procedure of, 40procedures, 40process, 4communication, 12{17module, 8name, 79termination, 29process, 19block, 96program, 15checked, 23program, 15program literal, 50program value, 71, 80punctuation, 73, 74quali�er, 80, 81RangeError exception, 29, 106, 111,112real literal, 73type family, 103real arithmetic, 103receive, 5, 9, 123record, 107record de�nition, 43rem, 104remove, 29, 42, 111repeat clause, 97reserved words, 74resolution, 75of base variables, 76of component names, 77of type names, 77result variable, 100return, 5, 9, 24, 123exception, 78reveal, 63, 117safe number, 103safe numbers, 103scalar type families, 101scope, 76, 80

218 C.1. Referencesof element variable, 109of inspect variable, 112security, 55selectno fair choice, 99select, 26abbreviation, 40select, 98{100selector, 41, 91, 109expression, 109long, 109mapping, 109semilattice, 57, 84meet, 57send, 118, 124send statement, 60server, 9server process, 25shared data, prohibited, 4shell, 9size of, 26, 114space, 73string literal, 50, 73, 109syntaxanalysis, 71systems programming, 12table, 28new, 109comparison, 108element typestate, 108insert, 29insert at, 29operations, 28representation independent, 28type family, 107unordered, 43table[expr], 42table[key], 43the-element, 42, 110tokens, 73typechecking, 8, 9, 17, 18, 72, 80{82, 150{153de�nition, 22, 77

families, 8inference, 72, 80{82inference rules, 55inferencing, 150{153name, 19speci�er, 21type, 127type name, 77resolution, 77type speci�er, 81, 82, 83o, 104typestate, 22case attribute, 62attributes, 23, 56, 78, 84{85checking, 9, 17, 24, 56, 72,84{92, 150{153checking algorithm, 90checking example, 89coercions, 87constraint attributes, 85entry, 23, 24errors, 91{92exit, 23exit formal, 119formal, 85{86initialization, 23minimum formal, 119postcondition, 90postcondition rules, 152precondition function, 89precondition rules, 152, 155semilattice, 84semilattice meet, 87syntax, 84{85valid, 86typestate, 127Uncopyable exception, 64, 94unique, 105unite, 116unite statement, 62unwrap, 66, 126unwrap statement, 84using list, 19

Appendix C. Prede�ned Module 219variablediscarded, 29�nalized, 29name resolution, 75{77overlap, 121temporary, 122variable namebase variable, 75component name, 75formal, 86variant, 61hidden, 61revealed, 61type family, 115typestate, 86visibility, 75where, 109where expression, 41where('true'), 47while, 10while, 97{98window system, 31word, 73, 74wrap, 66, 125wrapped value, 125wrapper, 125