atom
An atom is a mutable value associated with a module-allocated name. For each name that appears in the Names
section of a module's header, the compiler associates that name with a new atom. For each name that appears in the Uses
or Extends
section of a module's header, the compiler associates that name with the atom introduced by the module that named it. During compilation of a module, an atom may be retrieved by presenting its name, a string, to the primitive method "atom for_"
. If the atom was neither introduced by the current module nor imported from another module, then "atom for_"
privately introduces a new atom into the current module; a subsequent send with the same argument within the context of the same module will produce the same atom.
Module "Example"
Uses
"Avail"
Names
/* Introduction of the name "foo" also introduces an atom. */
"foo"
Body
/* Obtain the "foo" atom introduced by the "Example" module. */
fooAtom ::= atom for "foo";
Assert: fooAtom's name = "foo";
Assert: fooAtom's issuing module = current module;
Assert: current module's name = "Example";
Assert: "foo" ∈ current module's public names;
/* Obtain the "function" atom imported from "Avail". It was ultimately introduced
* by a module named "Special Objects".
*/
functionAtom ::= atom for "function";
Assert: functionAtom's name = "function";
Assert: functionAtom's issuing module ≠ current module;
Assert: functionAtom's issuing module's name = "Special Objects";
Assert: "function" ∈ functionAtom's issuing module's public names;
/* Look up the atom associated with "squid pinkies". Big surprise, there isn't
* one yet. The atom returned is brand new, but associated with the current
* module (because that's what the compiler was building when "atom for_" ran).
*/
newAtom ::= atom for "squid pinkies";
Assert: newAtom's name = "squid pinkies";
Assert: newAtom's issuing module = current module;
Assert: current module's name = "Example";
Assert: "squid pinkies" ∉ current module's public names;
In ordinary use, the primary asset of an atom is its distinction from every other value, including an equivalently named atom that originated in a different module. Consider the values true
and false
from classical logic. The principal characteristic of each is its distinction from the other. In other words, true
and false
differ by identity rather than by some measurable quantity. In Avail, the values true
and false
are special atoms; they are issued by the virtual machine rather than some module. The type boolean
is simply the finite enumeration of these two atoms. For another example, consider a simple model of colors:
/* This is a helper method for creating color atoms. */
Method "a color called_" is
[
name : string
|
atom for name
] : atom;
/* Create an atom for each of the colors of the rainbow. In our very simple model,
* the colors are distinct only by identity. Red isn't yellow because it isn't;
* it's red. Atoms achieve this effect nicely without introducing any baggage.
*
* As an example of baggage, imagine a model that represented the rainbow colors
* using the integers 1 through 7. Colors and integers would then be type
* compatible. It would be possible to multiply two colors together using "_×_",
* which is clearly silly.
*/
red ::= a color called "red";
orange ::= a color called "orange";
yellow ::= a color called "yellow";
green ::= a color called "green";
blue ::= a color called "blue";
indigo ::= a color called "indigo";
violet ::= a color called "violet";
/* This method provides a type name for the enumeration of rainbow colors. */
Method "rainbow color" is
[
{red, orange, yellow, green, blue, indigo, violet}ᵀ
];
/* The size of the "rainbow color" type is 7 because it has exactly 7 instances.
* Why? Because each member atom is unique.
*/
Assert: |rainbow color| = 7;
/* "rainbow color" is a type like any other, so we can declare a variable
* that can only store its instances.
*/
aColor : rainbow color := green;
In addition to a name and an issuing module, an atom also comprises a collection of bindings, called properties. Since an atom has identity, it can acquire new properties, update existing properties, and lose properties altogether. A property is an association between another atom — the property key — and an arbitrary value — the property value. The properties of an atom are not enumerable, thus ensuring modular usage; an algorithm can only query or alter a property for which it possesses the key. A property is interrogated using the method "_[_]" ([atom, atom]→⊤)
, added or updated via "_[_]:=_" ([atom, atom, any]→⊤)
, and removed with "Remove_[_]" ([atom, atom]→⊤)
. For each of these methods, the first atom is the target and the second atom is the property key.
The following example extends the previous one by associating wavelengths to the rainbow color
atoms:
/* Introduce the "wavelength" atom. */
wavelength ::= atom for "wavelength";
/* Assign wavelengths to each of the rainbow colors. Assume that the current
* module imports "Dimensional Analysis", which exports "nm" (nanometers).
* Feel free to disagree with my spectral demarcation.
*/
red[wavelength] := [620..750] nm;
orange[wavelength] := [590..619] nm;
yellow[wavelength] := [570..589] nm;
green[wavelength] := [495..569] nm;
blue[wavelength] := [450..494] nm;
indigo[wavelength] := [420..449] nm;
violet[wavelength] := [380..419] nm;
/* A helper method to read the wavelength. */
Method "_'s⁇wavelength" is
[
color : rainbow color
|
color[wavelength]
] : any;
/* Because the atom type is not parametric on its property key or value types,
* a semantic restriction is necessary to get a property value back out without
* loss of strength.
*/
Semantic restriction "_'s⁇wavelength" is
[
color : rainbow color's type
|
[380..750] nm's type
];
/* This method looks up a color by its wavelength. (Note that Greek minuscule
* lambda (λ) represents wavelength.)
*/
Method "color for λ=_" is
[
λ : [380..750] nm
|
$body : rainbow color;
For each of rainbow color's instances do
[
color : rainbow color
|
/* If λ is an instance of the color's wavelength (a dimensioned
* integral type), then answer the color. By construction, one
* of these values must be uniquely correct.
*/
If λ ∈ color's wavelength then
[
Exit body with color
];
];
/* This should never happen. */
Raise an exception
] : rainbow color;
/* This color is yellow. */
Assert: color for λ=578 nm = yellow;
The type atom
has every atom as an instance. atom
is a complete type. Though its properties are analogous to the bindings of a map or the attributes of an object, atom
does not require (or permit) type parameters for the property key type or the property value type. These limitations are a direct consequence of mutability.
Note that special atoms cannot participate in the property mechanism. For instance, the special atom true
cannot be either the target or the property key of a property interrogation. It can be a property value, however.