Obix handles void values in a way that sustains the important "fail fast!" principle, as described below.
Any object reference (attribute, input argument, output argument, constant and variable) can basically be set to void, which means that no object is attached to the object reference.
By default, void values are not allowed for attributes, input arguments and output arguments.
This means that a program error is raised immediately whenever:
an object is created with any attribute set to void
a service is initialized with any attribute set to void
a command is called with any input argument set to void
a command returns with any output argument set to void
The ability to allow or disallow void values is defined through the object reference's property voidable which can be set to yes or no. The default value is no.
In the following example of a type that defines a product, the value of attribute price_in_cents is allowed to be void at runtime (voidable:yes), but all other attributes cannot be void.
type product_3 default_factory:yes attribute identifier type:positive32 end attribute name type:string end attribute price_in_cents type:positive32 voidable:yes end end
A compile-time error is generated whenever void is assigned to an attribute or input argument with property voidable set to no (default value).
For example, the following instruction used to create an object of the above listed type generates a compile-time error, because attribute name is not allowed to be void.
const product_3 product = fa_product_3.co_create ( & identifier = 123 & name = void & price_in_cents = void )
The rule "by default void values are not allowed" is built into Obix for the following reasons:
not allowing void values is what we actually want in most cases, so the default behavior is adequate.
the rule greatly sustains the "fail fast" principle, which is the fundamental principle applied again and again in Obix in order to help writing more reliable and maintainable code.
To illustrate this, suppose that type person has attribute brain. Suppose also that Obix's default behavior would be the same than those of most programming languages, which means that:
void values are allowed for attributes
attributes are mutable (i.e. their value can be changed after the object has been created)
Suppose finally that variable buddy contains an instance of person.
In that case the instruction
buddy.brain = void
wouldn't generate a compile-time error, and, even worse, it wouldn't generate a run-time error. A run-time error would only appear later whenever a feature of brain is needed, such as
buddy.brain.think
The big problem with this behavior is that the initial design error is not detected at compile-time, it is also not guaranteed to be detected at run-time, and if it is detected at run-time, debugging can get really difficult, because the effect of the error can appear late after the object's creation, and it can appear anywhere in the source code, not only in the part that contains the root of the error. Moreover, what makes this runtime error such a pain is that tracking down its cause can be very frustrating, because the displayed error message often contains little or no information about the error's 'birth' (usually a void value assigned much earlier in the code, possibly even in another package written by someone else).
![]() | Note |
|---|---|
Experience shows that executing a feature on a void object is certainly one of the most frequent run-time errors in languages that apply the opposite rule, namely: "void values are allowed by default". For example, most Java programmers would probably agree that the |
Whenever appropriate, Obix's standard library makes heavy use of the "void values are not allowed" principle. For example, when a void input value would result in an undefined, or questionable result, then void is simply not allowed. For instance, what should be the result of the boolean operation a or b, if b is void. Some languages just return false, others return void (or null). Obix follows the "fail fast!" rule and therefore doesn't allow the operation to be executed, because void is an invalid value for b.
As the permission for void values has to be explicitly specified in the source code (by writing voidable:yes), programmers are more aware of that fact and are thus encouraged to pay attention to it, for example by first checking an expression for void before assigning it to an attribute or input argument.
In case of a type, the permission for void is simply specified in the type's source code through the voidable property. Therefore, factories implementing types don't need to explicitly check for void values. Obix automatically takes care of that. For example, if a type's command input argument cannot be void there is no need for a check in the factories' implementation code.
![]() | Note |
|---|---|
In Java, for example, whenever an input argument for an interface's method is not allowed to be null, then there is no way to specify this in the interface. Hence, each class implementing that interface must check for null with an explicit statement for each input argument, such as: if (input == null) {
throw new IllegalArgumentException("input cannot be null");
} |
![]() | Note |
|---|---|
| There have been suggestions to completely eliminate void values by providing special objects used to represent void or inexistent values, so-called null objects. The argument is that this eliminates the need for testing for void, and also suppresses the NullPointerException. However, there are some rejoinders. First, the idea of an object which is not a 'real object' seems to be unnatural. It is like having in a garage a strange kind of car which is not a real car. Secondly, not discerning real values from fake values introduces new sources for bizarre errors which can be even more difficult to detect than errors due to void values, because the effect will possibly be more distanced from its cause. However, if a programmer likes the idea of fake objects or if there are some good reasons to use them, nothing prevents her from applying them in Obix. For example, it might be appropriate to inhibit a void value for an attribute of type list, and then assign an empty list instead of a void value whenever there are no items in the list. |
voidable” (attribute)voidable” (input/output argument)