The Avail Programming Language

Introduced Names Section

The Names keyword begins the introduced names section. The contents of this section are zero or more string literals separated by commas , (U+002C). Each string literal introduces an atom and exports it for use by downstream modules. For example:

Module "Dice" Uses "Avail" Names /* Types. */ "dice expression⁇", "dice expression⁇outcome", "dice group", "die", /* Definers. */ "a|an standard⁇_-sided die", "a dice group with_", "a die showing_", "_d|D_", "_(drop the⁇highest«_»)", "_(drop the⁇lowest«_»)", /* Evaluating dice expressions. */ "«using_,»roll_", /* Accessors and mutators (dice expression outcome). */ "integrate_and_with_", "_'s⁇detailed outcome", "_'s⁇detailed outcome::=_,result::=_", "_'s⁇result", "_'s⁇result::=_", /* Accessors (dice). */ "_'s⁇faces" Body

In this example, Dice is a module that defines services related to constructing and evaluating arbitrary dice expressions of the kinds prescribed by many role-playing game systems. As such, it provides dice-related services but does not consume them, i.e., it knows how to use dice but does not know why or when to do so. Dice is a service provider, e.g., library, and its introduced names section constitutes the catalog of its available services.

By including a name in the introduced names section, the compiler or executor creates a new atom and associates it with the name. Equivalent names imported from upstream modules are shadowed by the new name. This means that an operation that resolves a name to an atom will always prefer an atom created by a locally introduced name over one imported from an upstream module. Because of this rule, the following scenario is unambiguous despite the fact that Avail and Dimensional Analysis each export "_+_":

Module "Concatenating Plus" Uses "Avail", "Dimensional Analysis" Names "_+_" Body /* When the name argument of "Method_is_" is a string, then it resolves * the name to an atom before installing the new definition. In this * case, the new definition is installed for the local "_+_", not one * of the imported ones. */ Method "_+_" is [ a : string, b : string | a ++ b ];

Note that module constants and module variables cannot be exported via the introduced names section. This is because the definition of a module constant or module variable does not create a new atom or participate in name resolution, whereas the definition of a message does.

Module "Counter" Uses "Avail" Names /* This doesn't work, because "counter" is a module variable, and module * variables cannot be directly exported. */ "counter" Body counter : whole number := 0;

This is a design point of Avail. While it would have been easy to associate atoms with module constants and module variables, thereby making them available for export, doing so would have increased the scope of module-specific state. This, in turn, would encourage construction of spaghetti code.

It is, however, trivial for a programmer to overcome this limitation in exactly those circumstances in which she wishes to do so. First, define methods that reference the module constant or module variable. Then export the associated messages for use by downstream modules.

Module "Counter" Uses "Avail" Names "Increment counter", "counter" Body _counter : whole number := 0; Method "counter" is [_counter]; Method "Increment counter" is [atomically read ↑_counter and add 1] : ⊤;

By forcing the programmer to build an API around module-specific state, programmers are compelled to:

  • Design features rather than accrete them over time.
  • Consider issues of semantic abstraction and data encapsulation.
  • Consider issues of the cardinality of software features, i.e., the singleton anti-pattern.
  • Consider issues of concurrent access.

In the above example, only the capabilities of reading and incrementing the counter are provided, not the capability of setting it to an arbitrary value. This API guarantees the monotonicity of the (singleton) counter.

‹ Module Name Section | Return to Modules | Entry Points Section ›