Interfaces and Implementations¶
An INTERFACE declares a runtime-dispatched function surface. A separate
IMPLEMENTATION names the concrete functions used for one interface handle.
Interface values carry implementation identity, not an owned user object.
Interface declarations¶
Every interface entry uses member syntax (.name) and FUNCTION. Static
::name entries are rejected. A signature-only entry ends in ;; it describes
a required dispatch slot.
Parameters, return types, receiver forms, overloads, and procedure delegates follow the ordinary function rules. A signature-only entry declares its public argument names but cannot give one argument a different body-local name, because it has no body. Implementations may choose their own local names.
Comparison operator entries follow the same restriction as struct operators:
interfaces declare OPERATOR== or OPERATOR<=>, never the synthesized
!=, <, >, <=, or >= names.
Default method bodies¶
An interface entry may provide a body:
::integer_source INTERFACE
{
.read FUNCTION(): I32;
.offset FUNCTION(@ARG:value I32): I32
{
RETURN value + 100;
}
}
The body is the slot's default implementation. A concrete implementation may override it with a matching function. A default body may use body-local parameter names because it is itself executable code.
If a default method calls another interface member, that call still requires a
concrete slot at execution. A body that depends only on its parameters can run
without one when the interface type is DEFAULTABLE.
DEFAULTABLE¶
::optional_source INTERFACE DEFAULTABLE
{
.read FUNCTION(): I32;
.fallback FUNCTION(@ARG:value I32): I32
{
RETURN value + 100;
}
}
DEFAULTABLE adds an empty handle state. It permits default construction and
construction or assignment from NULL:
VAR source optional_source;
ASSERT(source!?);
ASSERT(source.fallback(4) == 104);
source := NULL;
ASSERT(source!?);
source?? means that a concrete implementation is present; source!? means
the handle is empty. An interface without DEFAULTABLE cannot be
default-constructed or initialized from NULL.
Calling a required dispatch slot needs a concrete implementation. Only a suitable default body can execute on an empty handle.
Implementation declarations¶
::integer_source_impl IMPLEMENTATION(integer_source)
{
::read FUNCTION(): I32
{
RETURN 7;
}
::offset FUNCTION(@ARG:value I32): I32
{
RETURN value + 1;
}
}
The parenthesized type must be an interface. Functions inside the body are
nested declarations and therefore use ::name. A concrete function is matched
to an interface slot by member name and callable signature. Its local parameter
names may differ, but its public argument names, parameter types, receiver
contract, and result must satisfy the slot.
Overloaded interface names create distinct slots. An implementation supplies the matching overloads independently. Missing or incompatible required slots make conversion from that implementation invalid. A slot with a default body does not require an override.
The implementation body may contain declarations beyond the exposed surface; only functions matching interface slots are callable through the interface handle.
Constructing and assigning handles¶
An implementation declaration is the value used to select its dispatch table:
VAR source optional_source := integer_source_impl;
ASSERT(source??);
ASSERT(source.read() == 7);
VAR copy optional_source := source;
ASSERT(copy.read() == 7);
Interface handles are copyable and assignable values. Copying preserves the
selected implementation identity. Assigning another implementation retargets
subsequent calls; assigning NULL clears a DEFAULTABLE handle.
Interface values can also be STATIC when their implementation and retained
dependencies satisfy the static-value contract.
Dispatch and access control¶
A call first resolves the interface overload using the ordinary argument rules,
then dispatches the selected slot through the handle's current implementation.
Default bodies and implementation functions obey the same privacy rules as
other declarations; interface entries can be individually PRIVATE, including
through a PRIVATE { ... } block.
Interfaces differ from Generics: an interface handle selects a
declared implementation surface, while an owning GENERIC stores and manages a
concrete object and GENERIC_REF refers to an existing object structurally.