Impl Blocks
impl blocks let you attach behavior directly to a type.
They are how Calibre gives a type methods and associated items.
This says that get_language belongs to CountryBase.
type CountryBase := struct { language : Language };
impl CountryBase { const get_language := fn (self : &CountryBase) -> Language => self.language;};
let country := CountryBase { language : Language.SPANISH };print(country.get_language());The first parameter usually acts like the receiver.
Common receiver styles are:
self : &Typefor shared accessself : &mut Typefor mutable accessself : Typewhen working with the value directly
where Type can either be Self or the name of the type.
By doing so you allow for the function to be treated as a static function.
This works for built-in types as well as user-defined ones. For example:
impl int { const days := fn (self : Self) -> Self => self * 24 * 60 * 60;};
print(5.days());An impl block can also contain associated functions and constants, not just instance methods.
impl CountryBase { const default_language := Language.ENGLISH : 1;
const english := fn -> CountryBase => CountryBase { language : Self.default_language };};Associated items are accessed through the type itself.
let country := CountryBase.english();Inside an impl block, Self refers to the type currently being implemented.
impl CountryBase { const clone_language := fn (self : &Self) -> Language => self.language;};Calibre also uses impl together with traits:
impl Person for User { const name := fn (self : &User) -> str => self.name;};That trait-specific form is covered in the next section on traits.
Use impl TypeName { ... } when you want methods or associated items that belong directly to a type, and impl TraitName for TypeName { ... } when you are implementing a trait.