Interfaces and extensions
Interfaces and extensions separate an operation’s contract from its implementation. They are useful for generic algorithms, methods on types you do not own, operator capabilities, and multiple explicit providers.
Declare an interface
Section titled “Declare an interface”An interface has an implicit subject type named Self:
interface IMeasure method magnitude(self : Self) -> IntendMembers marked method participate in dot-call lookup. An ordinary func
member remains a dictionary function and is called through a provider value.
The distinction matters most for open types, where
only interface method members become dynamically dispatched slots.
Implement it with an extension
Section titled “Implement it with an extension”interface IMeasure method magnitude(self : Self) -> Intend
datatype Distance extensions DistanceMeasure Distance(Int)end
extension DistanceMeasure : IMeasure for Distance method magnitude(distance : Distance) -> Int do distance._0 endend
func main() -> Unit do distance = Distance::Distance(12); Debug.trace(distance.magnitude());endThe extension must satisfy the interface member names and types after Self
is replaced with Distance.
Associated extensions
Section titled “Associated extensions”The datatype’s extensions DistanceMeasure clause associates that provider
with the nominal Distance type. Associated providers are considered when a
method or contextual capability is requested for that owner.
Association exposes provider metadata, not an ordinary source name. Keep an associated extension private unless callers also need to name that specific provider explicitly.
Contextual generic capabilities
Section titled “Contextual generic capabilities”interface IMeasure method magnitude(self : Self) -> Intend
datatype Distance extensions DistanceMeasure Distance(Int)end
extension DistanceMeasure : IMeasure for Distance method magnitude(distance : Distance) -> Int do distance._0 endend
func measure[T]( value : T, ?{extension Measure: IMeasure for T},) -> Int do Measure.magnitude(value)end
func main() -> Unit do distance = Distance::Distance(12); Debug.trace(measure(distance));endAt measure(distance), the compiler finds the associated
DistanceMeasure. Exactly one applicable provider is required. No provider is
a missing-extension error; several equally applicable providers are an
ambiguous-extension error.
Direct extensions
Section titled “Direct extensions”An extension without an interface can add methods directly to a subject type:
extension IntParity for Int method is_even(value : Int) -> Bool do value % 2 == 0 endend
func main() -> Unit do Debug.trace(42.is_even());endOnly method declarations participate in dot-call lookup. A direct
extension’s ordinary func members are callable through its provider value,
not as methods.
For the full func/method comparison, including receiver rules, open-type
dispatch, and value-level witnesses, see Open types.
Method lookup and ordinary fields
Section titled “Method lookup and ordinary fields”For receiver.name(arguments), a directly callable record field takes
priority. Otherwise, the compiler searches visible and associated extension
providers for a unique method whose receiver matches. Provider selection does
not use the expected result type to choose between ambiguous providers.
An exported extension from another module is not automatically active merely
because that module is visible. Use
enable extensions Module.Extension near the top of the consuming source
module when an unassociated provider must participate in local method and
contextual lookup. Activation is local to that source module and is not
re-exported.
The activation must name an exported extension in a visible module. It is
placed after an optional module header and before declarations. It does not
change the provider’s public signature and does not make the extension active
in files that import the current module.
The provider and consumer would normally be separate source files in the same package (or in visible packages). This complete two-file example shows the placement and the qualified name:
provider.nl:module Provider
export extension IntCompare : ICompare for Int method compare(left : Int, right : Int) -> Int do if left < right then -1 elsif left > right then 1 else 0 end endend
consumer.nl:module Consumer
enable extensions Provider.IntCompare
func compare_with_zero(value : Int) -> Int do value.compare(0)end
func main() -> Unit do Debug.trace(compare_with_zero(1));endThe activation must name an exported, unassociated provider. Associated
providers, such as an extension listed after a datatype’s extensions clause,
do not need an activation at each call site.
Explicit extension witnesses
Section titled “Explicit extension witnesses”When an API has more than one possible provider, or when the choice is part of the API’s meaning, pass the provider explicitly. A contextual question binds the selected dictionary under an uppercase name:
interface IFormat func format(value : Self) -> Stringend
extension DecimalFormat : IFormat for Int func format(value : Int) -> String do value.to_string() ++ ".00" endend
func render[T]( value : T, ?{extension Format: IFormat for T},) -> String do Format.format(value)end
func main() -> Unit do Debug.trace(render(12, ?{Format: DecimalFormat}));end?{Format: DecimalFormat} is an explicit answer: it bypasses provider
resolution for that question and passes the named extension dictionary to the
function. The witness is a normal immutable value inside the function, so its
ordinary dictionary members are called as Format.format(value). The
contextual question’s extension form, candidate resolution, and associated
providers are covered in Question parameters.
The standard library uses these same rules for arithmetic, equality, ordering, debugging, iteration, indexing, and conversions.
The standard equality, comparison, hashing, and debugging interfaces also associate providers that the compiler specializes for concrete data shapes. Continue with Structural capabilities to see how those providers recurse through records, arrays, and datatypes.
Next: Operator overloading.