Methods and `static fn`s
A member says what it is with two words - static belongs to the type, var may change - and a type has one namespace of members, so a field and a method can never share a name.
A member of a type is either a field, or a constant that holds a function - there is nothing else. Two words say
which: static means the member belongs to the type rather than to a value, and var means it may change.
Example#
type Rectangle {
width: Int
var scale: Int = 1
fn area(): Int {
width * height * scale
}
var fn grow(by: Int) {
scale = scale + by
}
static fn square(size: Int): Self {
Self size, height: size
}
height: Int
}
var rectangle = Rectangle 3, height: 4
print rectangle.area()
rectangle.grow 2
const bound = rectangle.area
print bound()
print Rectangle.square(5)Syntax#
fn <name>(<parameters>): <Type> { ... } // A method: it reads its receiver
var fn <name>(<parameters>): <Type> { ... } // A method that changes its receiver
static fn <name>(<parameters>): <Type> { ... } // Of the type: there is no receiver
static <name> = <value> // A constant of the typeRules#
A method does not list
self. Its parameter list is what the caller writes, andselfis put there for it.areareadswidthandheightwithout qualifying them, because they are members of the same receiver.var fnsays the method changes its receiver, the same word avarbinding and avarfield use. A call of one needs avarpath, and the editor underlines both the declaration and every call of it.staticsays the member belongs to the type and not to a value.Rectangle.square(5)never needs aRectangleto work on.static vardoes not exist: there is no global mutable state.selfstays an expression inside the body. Pass it on, writecopy(...)with it, compare with it - what is gone is only the parameter that announced it.A function type still names the receiver.
(self: Rectangle) => Intand(var self: Config) => Voidare what a receiver closure is, and a method read as a value has exactly that type:Rectangle.areais(self: Rectangle) => Int, whilerectangle.areais() => Int, already bound.A method is a constant of the type that holds a receiver closure, not state on the instance. There is no memory per value for it and no way to replace it at runtime, which is also why a
constvalue with methods stays plain data as far asEquals,HashandEncodeare concerned.A type has one namespace of members. A field and a method are both constants of the type, so nothing keeps two members of the same name apart the way an overload set would - a field and a method cannot share a name.
Does not compiletype Counter { var count: Int = 0 fn count(): Int { count } } // error: `count` is already declared in `Counter`Calling a function that a field holds always takes parentheses. A field's value might itself be callable (
onClick: () => Void), andonClick()calls it exactly the way any other method call would, through the same namespace.
What this is not#
A static fn is not a method with an optional receiver. A member either belongs to the type or to a value, and
the message names the other form either way.
type Rectangle {
width: Int
height: Int
static fn square(size: Int): Self {
Self size, size
}
}
const rectangle = Rectangle 3, 4
print rectangle.square(5)
// error: `square` is `static`: call it as `Rectangle.square(...)`A field is not a constant of the type. x: Int is a field, and const x: Int is refused because const is what a
field is without the word (Fields, rule 1); static x = 0 is a constant of the type. A const x = 0 inside
a type body is neither, and says so.
type Probe {
const x = 0
}
// error: `x` is neither a field nor a constant of the typeRelated#
- Declaring a type - fields and methods together, and what the constructor generates.
- Property commands - what a command call does to a field instead of a method.
- Verbs and participles - naming a mutating method against the one that returns a copy.
- Receiver closures - the function type a method as a value has.
- Traits - a list of members a type promises to provide.