Objects
Simple Object
Composition-based; no inheritance.
object Player:
// composed data
data: Transform t;
// object constructor
Player(t);
// methods (owning)
def move():
t.pos += t.dir * t.speed;
const def print_position():
print($"t.pos = {t.pos}\n");
def main():
Player player = Player(Transform((1.2, 3.4), (4.5, 6.7), 0.12));
player.move();
Object data is stored separately and is encapsulated; only functions of the object or its composed func components can touch it.
Func Components
// required data becomes implicit params
func Movement requires(Transform t):
def move():
t.pos += t.dir * t.speed;
const def print_position():
print($"t.pos = {t.pos}\n");
Call via the component name; the required data is passed first:
Movement.move(t);
Movement.print_position(t);
Desugars to def Movement.move(mut Transform t):. Required types must be unique (data types only); a pure func (no requires) works too, e.g. Math.add(10, 20).
Composition
object Dog:
data: Legs;
func: Run, Jump;
Dog(Legs);
object Bird:
data: Wings, Legs;
func: Fly, Run, Jump;
Bird(Wings, Legs);
d := Dog(...);
d.run();
d.jump();
b := Bird(...);
b.fly();
If an object misses a data component that an included func requires, it's a compile error.
Signatures
- Explicit signature is what you write; the implicit signature is the desugared one (
mut Transform -> void). - Required data is mutable by default; mark a function
const defwhen it doesn't mutate (const Transform t). - Object methods always take an implicit
selffirst:def Counter.inc(mut Counter self, n). Delegating works viaself.fn1();. - Calling a composed func on an object passes the object's data pointers:
obj.somecall()->FuncType.somecall(obj.$0, obj.$2).
Func Instances
A func component can be stored as an instance to form a view over an object's data:
// object o contains func Func
Func f = o;
// mutates o's data
f.inc(3);
o.print();
The only way to build a func instance is from an object containing the component. DIMA keeps the data pointers valid if the object goes out of scope.
Interfaces
interface Serializable:
// virtual function: declaration only
const def to_string() -> str;
// implements clause is used to define which interfaces this object implements
object Object1 implements(Serializable):
data: Data d;
Object1(d);
// concrete implementation
const def to_string() -> str:
return $"\{ x: {d.x} \}";
Interface instances give polymorphism; any implementing object works:
def serialize(Serializable s):
print($"s.to_string() = {s.to_string()}\n");
def main():
o1 := ObjectType1(...);
serialize(o1);
o2 := ObjectType2(...);
serialize(o2);
Linking matches on the explicit signature (() -> str), not the implicit one.