sysl

Structs and methods

Data with a name, then behaviour beside it — and a receiver that says how the method takes the instance.

A struct is its fields

struct Point
    x: int
    y: int
end Point

var p = Point(6, 7)

print("point:", p.x, p.y)
point: 6 7

Construction is positional and uses the type’s own name. There is no new, and no allocation has happened: p is a value, it lives in this frame, and it is exactly as big as its fields.

A struct is copied when you assign it, the same as any other value:

struct Point
    x: int
    y: int
end Point

var a = Point(1, 2)
var b = a

b.x = 99

print("a:", a.x, "b:", b.x)
a: 1 b: 99

That is worth pausing on if you are used to a language where every object is a reference. b = a copied the point. If you want two names for one object, you ask for it — and that is the memory chapter.

Methods

A member with a self receiver is a method. The sigil on self says how the method takes the instance, exactly as it would on any other parameter:

struct Vec2
    x: int
    y: int

    // A read that does not touch the original takes `self` by value.
    sum(self) -> int = self.x + self.y

    // `*self` is a raw pointer to the instance, so the writes land in the caller's value.
    shift(*self, dx: int, dy: int)
        self.x += dx
        self.y += dy
end Vec2

var here = Vec2(3, 4)

print("before:", here.sum())
here.shift(10, 20)
print("after:", here.sum())
before: 7
after: 37

self by value gets a copy, which is what a read wants and what stops a method that only reads from being able to write. *self is the receiver a mutating method takes, and it is spelled with the same sigil that means “raw pointer” everywhere else — because that is what it is.

There is no this and no implicit receiver: self is written, and a field access through it is written too.

Properties and associated functions

A member with no parameter list is a property — it reads like a field but is computed:

struct Rect
    w: int
    h: int

    area -> int = self.w * self.h
    perimeter -> int = 2 * (self.w + self.h)
end Rect

var r = Rect(3, 4)

print("area:", r.area, "perimeter:", r.perimeter)
area: 12 perimeter: 14

Note the missing parentheses at the call: r.area, not r.area(). The convention that goes with that spelling is that a property is cheap — anything that allocates or loops is a method, so that the parentheses warn you.

A member with no self at all is an associated function, called through the type name. It is where a named constructor goes, since the positional Rect(w, h) covers only one shape:

struct Rect
    w: int
    h: int

    area -> int = self.w * self.h

    square(n: int) -> Rect = Rect(n, n)
end Rect

print("square:", Rect.square(5).area)
square: 25

Next: memory — the three modes, and the chapter that makes sysl a different language rather than a different syntax.

Search

Esc
to navigate to open Esc to close