Classes

Defining a class

class Point {
    x: Int,
    y: Int
}

Fields are separated by commas. A trailing comma is allowed. Fields and method definitions can be interleaved in any order.

Empty class forms:

class Token
class Empty;
class Empty {}

Classes cannot be defined inside other classes (SyntaxError). Redefining an existing class name is a TypeError.

Instantiation (new)

def main() {
    let p = new Point { x: 3, y: 4 }
}

All fields must be provided. Providing the wrong fields, wrong count, or wrong types is a TypeError. For classes with no fields, new C, new C{}, and new C { } are all valid.

Field shorthand: if a local variable has the same name as a field, the name alone can be used:

class C { a: Int }

def main() {
    let a = 1
    print(new C { a }.a.Str())    // prints 1
}

Field access and assignment

Fields can be read and assigned through any variable — field mutation is independent of whether the variable itself is mut:

class A { a: Int }

def main() {
    let a = new A { a: 1 }
    a.a = 2
    print(a.a.Str())    // 2
}

Instance methods

Defined with self as the first parameter. self must not have a type annotation:

class Point {
    x: Int,
    y: Int,
    def magnitude_sq(self) Int -> self.x * self.x + self.y * self.y
}

def main() {
    let p = new Point { x: 3, y: 4 }
    print(p.magnitude_sq().Str())    // 25
}

Static methods

Methods without a self parameter. Called on the class name, not on an instance:

class Counter {
    count: Int,
    def zero() Counter -> new Counter { count: 0 }
}

def main() {
    let c = Counter.zero()
}

Calling instance methods statically by passing the instance explicitly is also valid:

class S {
    def f(self, msg: Str) Str { return msg }
}
def main() {
    print(S.f(new S, "abc"))    // abc
}

Default parameters on methods

Same rules as for standalone functions:

class Greeter {
    def say(self, msg: Str = "Hello") {
        print(msg)
    }
}

def main() {
    new Greeter{}.say()        // Hello
    new Greeter{}.say("Hi")   // Hi
}

Static methods as first-class values

Static methods can be stored in variables and used as first-class functions. Instance methods cannot:

class S {
    def g() -> 1
}
def main() {
    let g = S.g
    print(g().Str())    // 1
}

Operator overloading

See Operators for full details:

class Foo {
    x: Int,
    def + (self, other: Foo) Foo {
        return new Foo { x: self.x + other.x }
    }
}

Class composition

class Inner { value: Int }
class Outer { inner: Inner, tag: Str }

def main() {
    let o = new Outer {
        inner: new Inner { value: 42 },
        tag: "hello"
    }
    print(o.inner.value.Str())    // 42
    print(o.tag)                  // hello
}