Draft on: [Indigo]<Cedardocs>Language>CedarStyle.doc, press
Release on: [Indigo]<Cedar>Documentation>CedarStyle.doc, press
Last edited: by Mitchell, June 5, 1982 3:42 pm; by Horning, June 9, 1982 5:05 pm
Stylizing Cedar Programs
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 probably does have value.
The conventions presented here have been generally agreed upon by the Cedar programming community and represent current normal practice. However, they are not 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 normally 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 parts that illustrate the rule are highlighted like this.
Names
Capitalization
Capitalize the first letter of a name
if it identifies a module, procedure, signal, or type, otherwise the first letter is lower-case:
Fugleman: 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: ComplexSetCONS[Complex[real: 1.0, imag: 0.0], NIL];
Case shift alone should not be used to distinguish identifiers.
There is a single class of exceptions: if one has a type, Foo, it is okay to declare a variable of that type as foo:
badID, badId, bADid, BADid: INT  -- BAD!
complex: Complex;        -- acceptable
c: Complex;           -- acceptable
Qualification
Identifiers from interfaces should be qualified
by their interface names or abbreviations for them:
DIRECTORY
IOStream, Process, Rope, UserExec;
SimpleExample: MONITOR
IMPORTS IO: IOStream, Process, R: Rope, UserExec =   
. . .
ReverseName: UserExec.CommandProc = BEGIN . . . END;
execStream: IO.Handle;
Renaming an interface in the IMPORTS clause
is perfectly good practice, provided that you don't use both names in the program.
SimpleExample: MONITOR
IMPORTS IO: IOStream, Process, R: Rope, UserExec =   
. . .
execStream: IO.Handle;
backwordsName ← backwordsName.Cat[R.FromChar[R.Upper[userName.Fetch[i]]]];
Qualified OPEN,
i.e., the "OPEN abbreviation: name" form should be used instead of the unqualified "OPEN name" form.
Unqualified OPEN
is acceptable over the scope of an entire PROGRAM module only for the DEFINITIONS module that it 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.
Module Naming
All module 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: DEFINITIONS = . . .
ListSortRef: DEFINITIONS = . . .
Rope: DEFINITIONS = . . .
A PROGRAM/MONITOR module name
should have the suffix "Impl" if its name without the suffix would conflict with the name of an exported DEFINITIONS module.
ObjectSupportImpl: PROGRAM  -- name would conflict with ObjectSupport without Impl
EXPORTS ObjectSupport = . . .
ObjectMachinery: PROGRAM -- name doesn't conflict with ObjectSupport
EXPORTS ObjectSupport = . . .
FUN: PROGRAM = -- name conflicts with nothing since it exports nothing
CONFIGURATION modules have no common suffix on their names.
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
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
Interval types
with closed lower bounds and open upper bounds are preferred:
[0..upperLimit)     -- preferred form
FOR i IN [0..n) DO IF a[i]=a[n] THEN . . . ENDLOOP;
Positional notation
for argument lists, record constructors, and extractors is preferred if there is just a single argument or component. It is 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]];
The keyword form
for argument lists, record constructors, and extractors is preferred 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]];
Named procedure result fields
are preferred, especially if there is more than one:
CreateBarGraph:
PROC [x, y, w, h: INT, parent: ViewerClasses.Viewer, fullScale: REAL]
RETURNS [barGraph: ViewerClasses.Viewer]
FindItem: PROCEDURE[k: Key] RETURNS [v: Value, found: BOOLEAN];
Exceptions: SIGNALs and ERRORs
General
ENDCASE should be used to generate an ERROR or to handle "all other cases",
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.
Locally defined signals
that don't escape out of an abstraction are okay.
In DEFINITIONS modules
Declare ERRORs as ERRORs
Use a different name for each resumable SIGNAL (since these are rare).
Use stylized comments with each procedure
in an interface that generates exceptions indicating which exceptions it generates 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
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: transform them into ones that are part of your interface (i.e., insulate your clients from details of your implementation).
In-module documentation:
The first two lines of a Mesa source module
should be comments, one giving the file name for the module, and the other giving the name of the person who last edited it and when she/he did so.
-- 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 User Exec.
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.
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.
Program layout
Use Mesa.abbreviations
The Fuglemen
This section consists of a set of skeletal programs, Fugleman, FuglemanImpl, and FuglemanClient, that employ and exemplify 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.