User Tools

Site Tools


ulam_programming_conventions

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:

Evaporator.ulam
/** \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:

Announcer.ulam
  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.

ulam_programming_conventions.txt · Last modified: 2015/05/26 09:28 by ackley