This is the root type of Avail. Every type is a subtype of void. Arguments to a block or method can not be of type void. That means if some method m has a return type of void, then every call to m must be in the form of a stand-alone expression (a statement). This is how the traditional distinction between procedures and functions is made. No variable may be typed as void, otherwise its value could not be extracted (except as a statement, which would be useless).
The only statement that can have a type other than void is the last statement in a block (and only if you want its value to be returned from the block). This prevents accidentally ignoring a return code, or expecting a function to have a side effect when it really just returns something. If you really want to call a method that has a non-void result and you don't wish to do anything with the result, you can use the "Discard_" method, which takes an argument (of type all or list) and returns void. For example, "Discard 1+2;" will compute the value 3 and then do nothing with this value. See also the type hierarchy.
This type is directly under void. It represents the root of the hierarchy of all ordinary objects. There are only a few specialized classes that don't inherit from all. See list for the most usual one.
Integers in Avail are unbounded. Optimizations exist for small integers, but these optimizations are transparent (except for speed) and platform-specific. Common arithmetic operations are provided, as well as bit manipulation (e.g., shifting and bit-wise logic). All bit-manipulation treats integers as sign extended indefinitely, so, for example, "2 or -4" is -2 (in binary, ...00010 or ...11100 is ...11110). Only arithmetic shifting is supported (no logical right shifts or rotations). If you really need to do rotations, you can simulate it using shifting and masks.
Extended integers include all integers, but also the two values INF and -INF. These values represent infinity and negative infinity, respectively. Thus, extended integer is a supertype of integer. These are really just two different integer ranges. Avail makes use of open and closed range notation from mathematics, so integer can also be written (-INF..INF), and extended integer can be written [-INF..INF]. In addition, natural numbers are [1..INF) and whole numbers are [0..INF). The byte type is written [0..255]. The range type that contains just the value 5 is written [5..5]. Attempting to use an expression like [5..4] produces an empty range which, of course, has no instances (members). Such an empty range expression produces the type terminates, which can be considered the range (INF..-INF) for purposes of bounds extraction. Note that an expression like (3..7) is just another way of writing [4..6], but [4..6] is the "preferred" (i.e., canonical) representation. This is relevant if you ask for the range's upper or lower bound, and ask if the bound is exclusive or inclusive. All finite bounds are automatically rewritten in inclusive form for convenience.
Some arithmetic and two's complement logical operations disallow infinity as an input. That's why their arguments are declared to take integers rather than extended integers. Looping with "_to_do_" allows INF as the second argument. This allows the decision to loop over either a finite sequence or an infinite sequence to be decided at run time. Also, the modulus operation "_mod_" is defined to accept INF in the right argument. If INF is actually passed, the result of the modulus is just the left argument.
The type boolean has two immediate subtypes: trueType and falseType. These have instances true and false. They are not numeric.
A container is a variable. It holds a value which can be changed. A container also has a type permanently attached to it that constrains what values it may contain. A 32-bit random hash value is also available for each container (not unique). A variable will sometimes contain no value. Attempting to extract a value from a variable with no value causes a runtime exception (which can be caught). Comparing two containers with "_=_" never considers the contents of the containers. Containers compare by identity only. Containers are the only things that can change in Avail, greatly simplifying the construction of many design patterns.
A tuple is just like the mathematical notion of a tuple - immutable and heterogenous. Provisions exist for describing tuple types that are semi-heterogenous and unbounded. You just specify the range of sizes an instance tuple can be (an integer type), what its initial element types are (a tuple of types), and what the type of all successive elements must be (a type). This covers fully homogenous tuples as a limit case (e.g., string is the same as tuple of character).
This type is special in that it does not inherit from all. It does inherit from void, though. Lists are generated automatically by the compiler whenever it sees a sequence of expressions separated by commas. Keeping all and list disjoint allows a clean mechanism of overloading of methods to accept one or more arguments. One version of the method accepts all (a single item), and the other version accepts a list (at least two items). This allows code to be specialized by quantity without preventing tuples, sets, and user defined collections from being passed as single objects.
In addition, this syntax is particularly convenient for constructing collections or passing a variable number of arguments. For example, to construct a tuple one uses either the "<>" method (for an empty tuple), or the "<_>" method, which has two implementations: taking an "all" as an argument or taking a list as an argument. Because all and list are disjoint, the decision can always be made statically as to whether one or many elements are to be in the tuple. Even if the compiler ignores this, at least the programmer can be sure of the intent when examining code. This would not be the case if list inherited from all, as a list might be passed in a case previously thought to always involve a single value (an all). Note that most data types do not allow lists as members. This makes certain reflexive operations tricky (e.g., examining stack entries), but I think it's worth it for the extra clarity in non-reflexive code.
Sets are unordered collections without duplicate elements. Sets are hashed for constant time membership testing. Like all objects in Avail except containers, sets are immutable. Traditional nondestructive set operations (e.g., union, difference, subset test) are provided as primitives for speed and convenience.
A set type contains information about how many members may be in its instances. It also contains an element type, which describes the types of its instances' elements.
Instances of map perform associative lookup. A map is a finite mapping from a set of keys (the domain) to values (the range). Maps are hashed (like sets) for speed. Two maps are considered equal if they have the same domains and each member of the domain is mapped to the same value in both maps. Some primitive map operations are provided for speed and convenience.
Like set types, a map type records a range of possible sizes. It also records the allowable type for keys and the allowable type for values.
A type is something that can be subtyped or instantiated (or the type terminates, for which neither is possible). Actually, a type is any object that is an instance of a subclass of type. For example, integer is a type. So is all. So is type. You can ask any object what its type is. For example, 5's type is integer, integer's type is primType, and primType's type is metaType. [false;]'s type is []->falseType, which is a subtype of closure and an instance of closureType. See also the type hierarchy.
A closure is a lexical closure in the style of Scheme (and many other languages). It is the equivalent of a block in modern Smalltalks, and is often called a block for simplicity.
A closure can be invoked with the "_()" or "_(_)" operator. The number of arguments within parentheses must match the number of arguments the closure is expecting, as should the types of the arguments. A closure has a type (an instance of closureType and a subtype of closure). A closure type A is a subtype of a closure type B if they have the same number of arguments and the corresponding argument types of A are supertypes of B and the return type of A is a subtype of the return type of B. Don't confuse this with the rules of method lookup, which complies with block typing but has its own additional caveats.
Besides _() and _(_), there is an _apply_ message that takes any closure and any tuple, typechecks them (and counts arguments) at runtime, and if ok it invokes the closure with the arguments. If the number of arguments or their types don't match what the closure is expecting, an exception (which can be caught) is raised. This is in the style of Smalltalk's #perform message, but still ensures runtime type safety.
Continuations are like a stack frame. They contain all the local variables, a little stack for assembling arguments to calls and other data shuffling, a program counter, and the closure being executed. They differ from stack frames in that they are immutable. Stepping through a continuation has the effect of creating a new continuation that strongly resembles the old one. In fact, if the continuation has no references except the process scheduler, it can be modified in place. This optimization is not allowed to visibly affect the apparent semantics of creating a new continuation for each nybblecode instruction executed.
A Process has a current continuation. As it executes, it keeps computing the continuation's successor and storing it back into the process.
This is a special abstract type. It is at the very bottom of the hierarchy, a subtype of every type in Avail. It represents the absence of the return of the flow of control in the same way void represents the absence of a useable value. A block or method whose return type is terminates does not ever return. It may, however, raise an exception, loop forever, or do anything else to avoid having to return normally. Because it doesn't return, a function with terminates for a return type can promise its invoker all the riches in the world if it ever returns, which it won't. That's why terminates is considered the most specific type.
Because terminates is the most specific type, it can also be used as an argument type in a block type (but not as a declaration type for an argument in an actual block). Because of the rule of contravariance of block argument types, a block type taking an argument of type terminates is more general than any similar block type taking any other type in place of terminates. The most general block type of one argument, for example, is [terminates]->void. Note that this is an abstract block type, in that no block could ever say its type was exactly [terminates]->void. That's because nobody could ever call it, so its statements would automatically be dead code.