====== ULAM Programming Conventions ======
===== Naming =====
==== Utilities vs Services ====
In the standard library, you can find things like ''WindowServices'' and ''DebugUtils'', both of which, viewed generally, have similar purposes: They provide methods you can call to do useful things. So, why are some named '*Services' while are named '*Utils'? Here's the key convention: Things suffixed with ''Utils'' should be size 0, while things suffixed with ''Services'' have a non-zero size.
=== The EventWindow and Random Exceptions ===
Of course the elephant in the standard library is ''EventWindow''. That name violates this convention, because it has neither 'Utils' nor 'Services' as a suffix; it gets away with the violation because it predates the whole naming convention. But, since it is size 0, we should //think// of it like ''EventWindowUtils'' and not ''EventWindowServices''.
Exactly the same is true for ''Random'', which we should think of as ''RandomUtils''.
==== Utils and Services Declarations ====
//Rule of Thumb: **Prefer data members for *Utils, and local variables for *Services.**//
Unless you have a specific reason not to, prefer to declare ''*Utils'' (which includes ''EventWindow'') as data members, and to declare ''*Services'' as local variables in a method. So, for example, prefer this:
/** \symmetries all */
element Evaporator {
EventWindow ew;
Void behave() { ew[0] = ew[1]; }
}
over this:
/** \symmetries all */
element Evaporator {
Void behave() {
EventWindow ew;
ew[0] = ew[1];
}
}
even though, in this case, both versions behave identically.
The reasoning for this **Make *Utils data members** guidance has both obvious and subtle points.
An obvious point is that since a *Utils is size 0, it doesn't impact the atomic bit budget at all, so it's harmless to make them data members, while conversely, a *Services data member //does// impact the bit budget, so we don't want to do it thoughtlessly. Furthermore, placing *Utils as data members makes them available to all methods with a single declaration, and also provides a systematic place (at or near the top of the class) for all *Utils declarations.
A subtle point is that even though a *Utils is size 0, when a *Utils method is called, the ''self'' object passed into the called method can differ depending on how the *Utils was declared. And that can affect how the called *Utils method behaves. For example, this definition:
element Announcer {
DebugUtils du;
Void behave() { du.printContext(); }
}
produces output like this:
20150526025906-256: 3AEPS [FFE892F7]MSG: @(31, 4) of [1,1]: (An)
20150526025907-257: 8AEPS [FFE892F7]MSG: @(31, 4) of [1,1]: (An)
20150526025907-258: 10AEPS [FFE892F7]MSG: @(31, 4) of [1,1]: (An)
in which the ''printContext'' method reports the event position as well as the symbol ''(An)'' of the ''self'' object it was called on. That's not terribly useful, but consider this very similar definition:
element Announcer {
Void behave() {
DebugUtils du;
du.printContext();
}
}
which produces output like this:
20150526030050-256: 2AEPS [FFE91807]MSG: @(17,24) of [2,1]: (E)
20150526030050-257: 3AEPS [FFE91807]MSG: @(17,24) of [2,1]: (E)
20150526030050-258: 4AEPS [FFE91807]MSG: @(17,24) of [2,1]: (E)
What has happened? How can printContext be reporting type ''(E)'' (for Empty) when we //know// there was an ''Announcer'' atom in the center of the event window -- there must have been, or else the Announcer's behave method wouldn't have been called? The answer is that when ''DebugUtils'' is declared as a local variable, it is not associated with the atom at the center of the event window, it is associated with a scratch atom living on the function call stack, that happens to have type Empty.
Many *Utils methods never examine their ''self'' object so this difference is moot. For example, neither ''EventWindow'' nor ''Random'' (both size 0 quarks) do. But some do, and
following the **Make *Utils data members** ensures that all *Utils method calls will be associated with caller's underlying storage, which is almost always either what you want, or of no harm.