CedarProgramStyle.tioga
Written by Jim Mitchell
Last Edited by Mitchell on December 20, 1982 5:28 pm
Last Edited by Beach, May 9, 1984 5:01:14 pm PDT
Last Edited by: Subhana, May 30, 1984 12:06:56 pm PDT
STYLIZING CEDAR PROGRAMS
STYLIZING CEDAR PROGRAMS
CEDAR 5.2 — FOR INTERNAL XEROX USE ONLY
CEDAR 5.2 — FOR INTERNAL XEROX USE ONLY
Stylizing Cedar Programs
Cedar Version 5.2
Jim Mitchell
Release as [Indigo]<Cedar5.2>Documentation>CedarProgramStyle.tioga
© Copyright 1984 Xerox Corporation. All rights reserved.
Abstract: styl"ize tr.v. -ized, -izing, -izes.
1. To subordinate verisimilitude to principles of design in the representation of.
2. To represent conventionally; conventionalize.
This memo is probably out of date if it is in hardcopy form. It documents Release 5.2 of Cedar during May 1984.
[If you are reading this document on-line, try using the Tioga Levels and Lines menus (if you can) to initially browse the top few levels of its structure before reading it straight through.]
XEROX Xerox Corporation
Palo Alto Research Center
3333 Coyote Hill Road
Palo Alto, California 94304
For Internal Xerox Use Only
Introduction to Cedar: Contents
0. Introduction
1. Names
1.1. Capitalization
1.2. Qualification
1.3. Module Naming
2. Types
3. Exceptions
: SIGNALs and
ERRORs
3.1. General
3.2. In DEFINITIONS modules
3.3. In PROGRAM modules
4. Programming constructs:
5. Module histories:
6. Program layout
7. The Fuglemen
7.1. Fugleman.mesa
7.2. FuglemanClient.mesa
7.3. FuglemanImpl.mesa
0. Introduction
styl"ize tr.v. -ized, -izing, -izes. 1. To subordinate verisimilitude to principles of design in the representation of. 2. To represent conventionally; conventionalize.
Well, maybe we don't want to "subordinate verisimilitude to principles of design," but using some set of reasonable conventions in Cedar programs certainly does have value.
The conventions presented here have been generally agreed upon by the Cedar programming community and are approximations to current practice. Consequently, they cannot be hard and fast rules, and there can be good and compelling reasons for not following some convention in a particular instance. Nevertheless, to the extent that we all usually follow them, it will help us in reading (and therefore in modifying) one anothers' programs.
This section has two major parts: The first consists of a set of conventions for constructing Cedar identifiers according to their intended uses, for constructing types, for declaring and using SIGNALs and ERRORs, for documentation embedded in modules, for program layout (indentation, etc.), and for object-oriented programming. The second part consists of a set of skeletal programs that employ and exemplify these conventions.
In the first part, each convention is given as a short rule followed by some examples of its application. In the examples, the rules are highlighted like this while the parts that illustrate the rule are highlighted like this.
1. Names
1.1. Capitalization
Capitalize the first letter of a name
if it identifies a module (interface or implementation), procedure, signal, or type, otherwise the first letter is lower-case:
Fugleman: CEDAR DEFINITIONS = ...
Factorial: PROC[i: INT] RETURNS [INT];
Complex: TYPE = RECORD[real, imag: REAL];
NarrowRefFault: ERROR;
Capitalize the first letter of each embedded word
of a multi-word name:
NarrowRefFault: ERROR;
ComplexSet: TYPE = LIST OF Complex;
aSet: ComplexSet ← CONS[Complex[real: 1.0, imag: 0.0], NIL];
Case shift alone should not be used to distinguish identifiers.
There are some exceptions: if one has a type,
Foo, it is okay to declare a variable of that type as
foo; it is acceptable to name a condition variable and a
BOOLEAN describing the condition similarly:
badID, badId, bADid, BADid: INT -- BAD!
complex: Complex; -- acceptable
c: Complex; -- acceptable
1.2. Qualification
Identifiers from interfaces should be qualified
by their interface names or abbreviations for them:
DIRECTORY
IO, Process, Rope, UserExec;
SimpleExample:
CEDAR MONITOR
IMPORTS IO, Process, Rope, UserExec =
. . .
ReverseName: UserExec.CommandProc = BEGIN . . . END;
execStream: IO.Handle;
Renaming an interface in the
IMPORTS clause
is okay, provided that you don't use both names in the program.
SimpleExample:
CEDAR MONITOR
IMPORTS Process, Rope, X: UserExec =
. . .
ReverseName: X.CommandProc = ...
The qualified/renaming form of
OPEN should be used
instead of the unqualified form.
OPEN G: Graphics;
There are a few valid reasons for using an unqualified OPEN:
It is acceptable over the scope of an entire PROGRAM module only for the DEFINITIONS module that the module exports. It is also okay to OPEN any interface over a limited scope (e.g., a procedure or a block) within which identifiers from that interface are used heavily. It is never okay to unqualifiedly open anything except an interface.
1.3. Module Naming
All module source file names have the extension ".mesa".
ObjectSupport.mesa
ObjectSupportImpl.mesa
ListSortRef.mesa
SimpleExample.mesa
A
DEFINITIONS module does not need any standard suffix on its name.
ObjectSupport: CEDAR DEFINITIONS = . . .
ListSortRef: CEDAR DEFINITIONS = . . .
Rope: CEDAR DEFINITIONS = . . .
A
PROGRAM/
MONITOR module name should have the suffix "Impl"
if its name without the suffix would conflict with the name of a
DEFINITIONS module.
ObjectSupportImpl:
CEDAR
PROGRAM
-- name would conflict with ObjectSupport without Impl
EXPORTS ObjectSupport = . . .
ObjectMachinery:
CEDAR
PROGRAM
-- name doesn't conflict with ObjectSupport
EXPORTS ObjectSupport = . . .
Fun: CEDAR PROGRAM = -- name conflicts with nothing
A
CONFIGURATION module does not need any standard suffix on its name.
Rigging:
CONFIGURATION
EXPORTS Rope = . . .
Spy:
CONFIGURATION
EXPORTS SpyOps = . . .
Compiler:
CONFIGURATION
EXPORTS ExecOps, CompilerOps = . . .
A
CONFIGURATION file name should have the extension ".config".
Rigging.config
Spy.config
Compiler.config
2. Types
Name types
rather than defining anonymous types, especially in definitions modules:
DeckIndex: TYPE = [0..52); -- YES
CardDeck: TYPE = ARRAY DeckIndex OF Card;
CardDeck: TYPE = ARRAY [0..52) OF Card; -- NO
Use closed lower bounds and open upper bounds
for interval types:
[0..upperLimit) -- preferred form
FOR i IN [0..n) DO IF a[i]=a[n] THEN . . . ENDLOOP;
Use positional notation for single-component argument lists,
record constructors, and extractors. Positional notation is also acceptable if there are multiple components, all of different types:
ViewerTools.SetSelection[handle.fact.input]; -- force the selection
execStream.PutF["Your user name backwards is: %g\n", IO.rope[backwordsName]];
Use the keyword form
for argument lists, record constructors, and extractors when there are two or more constituents, especially if any have equivalent types. It is also preferred when most components are being defaulted and only a few given values:
context.DrawBox[
Box[xmin: 0, ymin: 0, xmax: data.value, ymax: self.ch]];
Menus.AppendMenuEntry
[
menu: my.outer.menu, -- the outer container's menu (already defaulted)
name: "MyMenuEntry", -- name of the command
proc: MyMenuProc, -- proc associated with command
fork: TRUE, -- causes a new process to be forked & detached on invocation
copy: TRUE ]; -- make this a new menu (i.e. don't modify the default menu)
[in: in, out: out] ← IO.CreateTTYStreams[title];
R.Compare[s1: rope, s2: NARROW[r2, ROPE], case: TRUE]
NEW[ViewerClasses.ViewerClassRec ← [paint: PaintGraph]];
Name procedure parameters
in the definitions of procedure types in
DEFINITIONS modules:
CommandProc: TYPE = PROC [exec: ExecHandle, clientData: REF ANY ← NIL] RETURNS[ok: BOOLEAN ← TRUE];
Name procedure result fields,
especially if there is more than one:
CreateBarGraph:
PROC [x, y, w, h:
INT, parent: ViewerClasses.Viewer, fullScale:
REAL]
RETURNS [barGraph: ViewerClasses.Viewer]
FindItem: PROC[k: Key] RETURNS [v: Value, found: BOOLEAN];
3. Exceptions
: SIGNALs and
ERRORs
3.1. General
Use
ENDCASE only to generate an
ERROR or to treat "none of the above",
but not to handle specific cases.
Use
SIGNAL or
ERROR when generating a
SIGNAL or
ERROR;
e.g., write "
SIGNAL foo" or "
ERROR foo", not just "foo".
Avoid using the anonymous
ERROR.
except for "impossible" conditions.
Locally defined signals shouldn't escape from an abstraction
and need not conform to the conventions below.
3.2. In
DEFINITIONS modules
Declare
ERRORs as
ERRORs and
SIGNALs as
SIGNALs.
Don't declare a
SIGNAL and then generate an
ERROR with it.
Try to define a small number of ERRORs and SIGNALs in an interface.
Normally define a single ERROR/SIGNAL with a parameter to distinguish the exact reason for it. Only use separate names for separate signals when they must have different types for reasons of resumption:
IOError: ERROR [ec: ErrorCode];
ErrorCode: TYPE = {NotImplementedForThisStream, IllegalPutBack, SyntaxError, StreamClosed, FileNotFound, FileAlreadyExists, IllegalFileName, WrongTransactionType, FileTooLong, BadIndex};
IOSignal: SIGNAL [ec: SignalCode];
SignalCode: TYPE = {EmptyBuffer, UnmatchedLeftParen, UnmatchedStringDelim, Rubout, UnprintableValue, TypeMismatch, UnknownFormat};
BufferOverFlow: SIGNAL[text: REF TEXT] RETURNS[REF TEXT];
-- used by text streams, should return a bigger one, with characters copied over, or else can return same text with length reset after having done something with chartacters.
Indicate
which SIGNALs/
ERRORs are generated by a procedure
in the comments of an interface and what happens if a given
SIGNAL is
RESUMEd:
FindItem: PROC[k: Key] RETURNS [v: Value, found: BOOLEAN];
-- ERRORs: HashTableDamaged
-- SIGNALs: HashTableProblem[tableFull]: tries again if resumed
GetRefAny: PROC [stream: STREAM] RETURNS [REF ANY]; -- from IO
-- SIGNALs: IOSignal[UnmatchedLeftParen, UnmatchedStringDelim]; resumption causes GetRefAny to supply the missing right parenthesis or quote.
3.3. In
PROGRAM modules
Only exceptions that are part of the abstraction should emanate from a module.
Don't let exceptions that are "part of" the interfaces of invoked routines escape: handle them completely within your abstraction, or transform them into exceptions that are part of your interface (i.e., insulate your clients from details of your implementation).
... inputNumber ← Convert.IntFromRope[contents -- from SampleTool
! SafeStorage.NarrowFault => {inputNumber←-1; CONTINUE}];
4. Programming constructs:
Ensure that the target type for a
NARROW is obvious
either by naming it explicitly or using it in a simple context.
handle: Handle ← NARROW[clientData]; -- target type = Handle
a: INT = IO.GetInt[IO.RS[NARROW[first, ROPE]]]; -- target type = ROPE
Use
TRUSTED over the most confining region in which it is needed.
TRUSTED {Process.Detach[FORK EvalLoop[ ]]};
5. Module histories:
The first few lines of a source module
should be comments, the first giving the file name for the module, and the following giving the names of the people who last edited it and when they did so. If you use the conventions described in the Tioga chapter for these lines, Tioga can be made to update the last-edited date automatically when you save the file.
-- OnlineMergeSortRefImpl.mesa
-- Last Edited by MBrown on March 9, 1982 5:02 pm
Immediately following each procedure declaration in a definitions module
should be a brief comment, providing the information most useful to a potential client, who may be reading the listing, or querying via the Interpreter.
Sort:
PROC
[itemList: LIST OF Item,
compareProc: PROC[Item, Item] RETURNS [Comparison]]
RETURNS[LIST OF Item];
-- Destructive sort of itemList; returns sorted list containing same items.
-- Order of equal items is not preserved.
Keep a
CHANGE LOG
at the end of each source module for keeping track of who first created the module, who has changed it, why it was changed, and when. (see the description of the Tioga ChangeLog button in the Tioga chapter for a way of preparing change log entries semi-automatically).
CHANGE LOG
Created by MBrown on 21-Apr-81 13:26:10
Changed by MBrown on 19-Aug-81 15:14:34
-- CedarString -> Rope (used only in Compare.)
Changed by MBrown on 23-Aug-81 17:45:12
-- Fix bug in sorting NIL.
Changed by MBrown on 10-Dec-81 9:57:58
-- Cosmetic changes: ROPE, INT.
Changed by MBrown on March 9, 1982 5:03 pm
-- Use a bounded array in local frame instead of consing up a list of lists on each call. Even for a 32 bit address space, this is only 60 words of array in the frame.
6. Program layout
Use Cedar.abbreviations
and see the description of the "Expand Abbreviation" command in the Tioga chapter.
Use Tioga Mesa formatting
for Mesa keywords, comments, and procedure names. See the description of the "Automatic Mesa formatting" feature in the Tioga manual.
7. The Fuglemen
This section consists of a set of skeletal programs, Fugleman, FuglemanImpl, and FuglemanClient, that use the conventions expounded in the above style discussion. The name Fugleman comes from the following definition:
fu"gle"man n., pl. -men.
1. Archaic. A soldier who serves as a guide and model for his company.
2. A leader; especially a religious leader.
And, Lord knows, there is no more religious issue than one's favorite programming style.
7.1. Fugleman.mesa
This is a template for defining the interface for a class, Fugleman, of objects. It provides for multiple, coexisting implementations of the class. The file FuglemanClient.mesa shows how a client program can create instances of the objects of this class, and the file FuglemanImpl.mesa is a template for an implementation of the class.
-- Fugleman.mesa
-- Last Edited by Horning, June 7, 1982 2:55 pm
Fugleman: CEDAR DEFINITIONS =
BEGIN
Handle: TYPE = REF Rep;
Rep: TYPE; -- opaque type for the representation of the object
FugleFailure: ERROR;
NewFugleman: PROC RETURNS [Handle];
-- Makes a new Fugleman object
Print: PROC [h: Handle];
Check: PROC [h: Handle];
-- ERRORs: FugleFailure
-- Returns normally if Fugleman object OK
SomeProc: PROC [h: Handle, otherArgument: INT] RETURNS [INT];
-- Comment goes here saying what it does
END.
CHANGE LOG
7.2. FuglemanClient.mesa
This is an example intended only to show syntactically how client programs create object instances, invoke operations on them and catch signals that those operations might generate. The files Fugleman.mesa and FuglemanImpl.mesa contain the definitions of the object interface used here and a sample implementation of it, respectively.
FuglemanClient.mesa
Last Edited by Mitchell, August 16, 1982 2:04 pm
DIRECTORY
Fugleman USING [Handle, NewFugleman, Check, SomeProc, FugleFailure];
FuglemanClient:
CEDAR PROGRAM
IMPORTS Fugles: Fugleman =
BEGIN
TestError: ERROR;
SimpleTest:
PROC =
BEGIN
a nonsense program to show how clients invoke operations on objects implemented this way
fm: Fugles.Handle = Fugles.NewFugleman[];
anotherFm: Fugles.Handle = Fugles.NewFugleman[];
x: INT ← 1;
fm.Check[ ! Fugles.FugleFailure => ERROR TestError]; -- check the object, convert error
IF fm=anotherFm THEN ERROR TestError; -- should get unique objects
IF fm.SomeProc[1] = anotherFm.SomeProc[2] THEN x𡤂
END;
SimpleTest[ ]; -- call the test procedure
END. -- FuglemanClient
CHANGE LOG
7.3. FuglemanImpl.mesa
FuglemanImpl.mesa
Last Edited by Mitchell, August 16, 1982 2:01 pm
FuglemanStdImpl:
CEDAR PROGRAM
EXPORTS Fugleman =
BEGIN OPEN Fugleman;
Implementation types
Handle: TYPE = REF Rep; -- declare the concrete type locally
Rep:
PUBLIC TYPE =
RECORD[
count: INT,
flag: BOOLEAN ← TRUE,
mumble: INT];
FugleFailure: PUBLIC ERROR = CODE;
NewFugleman:
PUBLIC PROC
RETURNS [Handle] =
BEGIN
RETURN [NEW[Rep ← [count: 5, mumble: -10]]];
END;
Operations on a Fugleman
Print:
PUBLIC PROC [h: Handle] =
BEGIN
-- some body would go here
END;
Check:
PUBLIC PROC [h: Handle] =
BEGIN
IF h.count>100 OR h.mumble<0 AND NOT h.flag THEN ERROR FugleFailure; -- example only
END;
SomeProc:
PUBLIC PROC [h: Handle, otherArgument:
INT]
RETURNS [
r: INT] =
BEGIN
IF otherArgument>100 THEN otherArgument ← 100;
h.count ← otherArgument;
r ← 17;
END;
END. -- FuglemanStdImpl