Guards provide declarative dispatching of function variants consistent with the
declarative style of functional programming. Using guards for dispatching
has many benefits: function variants are self-contained and focus on a single
task (separation of concerns), design-by-contract is inherent, data manipulation
is separate from computational logic, self-documenting.To use the 'guard' function, first the abstract function must be declared
with 'UseFunction'. All concrete variants are then guarded by a guard
statement followed by the actual function definition. The guard statement
tells futile.paradigm under what conditions this particular function variant
should be called. This is defined by passing an expression to the guard
function that operates on the arguments of the concrete function (see Examples).
Guards are naturally scoped based on the number of arguments in a function.
Hence only functions with the same number of arguments as were passed into the
parent function will be considered for dispatching. Guards can also be defined
in a short-hand as an expression that is dynamically bound to the actual
function implementation at run-time.
When using strict guards (the default behavior) named arguments passed into the
parent function must match the named arguments defined in the child function.
Hence, dispatching is defined by both the number of arguments and the matching
named arguments, which introduces a greater level of control in dispatching
than otherwise possible. In general, the built-in argument matching will Do
the Right Thing w.r.t. unnamed arguments, although the first match will be the
one applied, which could result in unexpected behavior if one is careless.
Note that when strict argumentss are applied, the names of the arguments in the
guard functions are irrelevant. The strict matching occurs at the level of the
actual functions and not the guard functions.
When strict guards are disabled, then only argument length will be applied as a
precondition plus any conditions defined by the guards. In certain circumstances
this may be desired, although in general strict guards provides greater control.
The order that the function guards are defined determines the order that
functions are evaluated for satisfaction of guard criteria. This behavior is
typically called top-to-bottom, left-to-right evaluation. The left-to-right
evaluation only applies to the explicit form when multiple function guards are
defined in a vector. These details are important to understand as default
functions defined too early will be greedy and no other criteria will be
evaluated.
Another important consideration is that using the ellipsis argument is not
supported in futile.paradigm. This is by design as the functional programming
approach is intentionally making function arguments explicit, such that the
ellipsis argument should never be needed in a child function definition.
The 'isStrict' function is an introspective function that indicates whether
the given child function has strict guards or not.
The 'register' function is used to manually register environments that a
function resides. This is necessary for package development where the calling
environment doesn't seem to be registered properly.
The 'isa' function is used to perform type checking. Calling the function checks
whether 'argument' is a 'type'. This is more than syntactic sugar
in guard sequences as it adds a level of protective indirection when accessing
type information. Note that the preferred format is to supply a raw symbol to
isa, and following naming conventions, it is easy to infer the meaning from
the syntax without undue clutter.