Extensions
extend adds methods to a type after its declaration, or adds an explicit trait implementation for
an existing type. Use extensions to keep a type’s core definition separate from optional behavior.
Adding Methods
Section titled “Adding Methods”struct User { public Int id;}
extend User { public Int id_value(self) { self.id }}
Int value = user.id_value();Extension members follow ordinary member visibility. Write public on methods that other modules
should call.
extend does not currently support init or deinit.
Adding Trait Implementations
Section titled “Adding Trait Implementations”extend Type: Trait { ... } adds a trait implementation:
trait HasValue { Int value(self);}
struct User { public Int id;}
extend User: HasValue { public Int value(self) { self.id }}A type does not satisfy a trait just because it happens to have methods with matching signatures.
The implementation must be declared on the type or in extend Type: Trait.
Generic Extensions
Section titled “Generic Extensions”Extensions can be generic:
struct Holder<T> { public T value;}
extend <T> Holder<T> { public T get(self) { self.value }}Use @where to make an extension available only for selected type arguments:
@where(T == Int)extend <T> Holder<T> { public Int only_int() { 7 }}
Int value = Holder<Int>.only_int();Type-Shape Matching
Section titled “Type-Shape Matching”@where can match concrete type shapes. _ is an anonymous placeholder, accepting any type
argument in that position:
@where(T == _^)extend <T> Holder<T> { public Bool stores_box() { true }}
@where(T != _?)extend <T> Holder<T> { public Bool stores_non_optional() { true }}Use : for trait bounds. Use == or != for concrete type-shape matching:
@where(T: Sequence<Element = UInt8>)Int count_bytes<T>(T value);
@where(T == _^)Int box_only<T>(T value);The extension target itself can also use anonymous placeholders:
extend <T> T& { public Bool is_reference() { true }}
extend <T> T[]^ { public Bool is_owned_slice() { true }}Compiler-owned type names are hidden. Extensions target the surface forms directly, including
generic patterns such as extend <T> T& and extend <T> T[]^.
Module Visibility
Section titled “Module Visibility”Extensions can be used across module boundaries, but they are not globally active. A file must import the module that defines the extension before extension members participate in lookup.
public struct Holder<T> { public T value;}
@where(T == Int)public extend <T> Holder<T> { public Int only_int() { 11 }}import ext = "holder_ext.jiang";
alias ImportedHolder = ext.Holder;
Int value = ImportedHolder<Int>.only_int();Use public extend when an extension should be available to modules that import it. An extension
without public is only visible inside the module that defines it.
Jiang does not impose a global orphan rule on extensions: a module may extend a public external or builtin type. Lookup only considers extensions visible from the use site. A concrete target is more specific than a generic target; if multiple callable extensions with the same specificity match, the call is ambiguous rather than being selected by declaration order.
public import can re-export public extensions from another module:
public import leaf = "holder_ext.jiang";import mid = "mid.jiang";
alias Holder = mid.leaf.Holder;
Int value = Holder<Int>.only_int();