Skip to main content

Classes

The Alia language supports object-oriented programming; classes are defined with the class keyword.

1. Defining Classes​

Basic Syntax​

class <Key name> <Object body> ~ class definition
class <Key name> extends <Key parent> <Object body> ~ class inheritance
class <Key name> *[key: value, ...] ~ mutable class (trailing `...`)

Example:

class Person *[
name: ''
age: 0
new: alias <String name> <Number age> -> {
self.name = name
self.age = age
self
}
]

The new method returns self, the current instance object; the result of instantiation is determined by this return value.

Class Expressions (Anonymous Classes)​

When the class name is omitted, class *[body] is evaluated as an expression and returns the class body object (an anonymous class), often used together with let/const assignment:

const Greeter = class *[
name: String
new: alias <String n> -> {
self.name = n
self
}
greet: alias -> {
'Hello ' .. self.name
}
]

const g = new Greeter ['Alia']
print (call g.greet) ~ Hello Alia

Mutability is declared by a trailing ... in the class body (there is no mut modifier any more). A class whose body ends with ... allows adding/removing instance properties; by default the structure is fixed.

class Point *[
x: 0
y: 0
...
]
const p = new Point
p.x = 5
print p.x ~ 5
p.z = 1 ~ Point is mutable, so new properties can be added

Array/object literals use the same trailing ... marker to declare a variadic (mutable) container:

const nums = [0, 1, 2, ...] ~ mutable array
const bag = *[a: 1, ...] ~ mutable object
note

mut {} / mut *{} (mutable stacks) are unaffected — they are unrelated to type mutability.

2. Instantiating Classes​

The new keyword instantiates a class and supports three ways of passing arguments: no arguments, array arguments, and object arguments:

class Person *[
name: ''
age: 0
new: alias <String name> <Number age> -> {
self.name = name
self.age = age
self
}
]

const p1 = new Person ~ no-argument instantiation
const p2 = new Person ['Alice', 30] ~ array arguments: passed in constructor parameter order
const p3 = new Person *[name: 'Alice', age: 30] ~ object arguments: passed by constructor parameter name

In object-style invocation, keys first match constructor parameter names and are forwarded; keys that match no parameter are assigned onto the instance after construction. When the class defines no new method, every key initializes an instance member directly:

class Point *[x: Number, y: Number]

const p = new Point *[x: 3, y: 4] ~ no constructor: keys land on the instance

Class body members may declare type annotations. An annotation evaluates to Null at runtime and is consumed only by the type checker and formatter:

class BigNum *[
digits: [Number, ...] ~ self.digits[i] checks as Number; variadic ops like push work
tag: Number | String
fallback: Number?
]

When constructor parameters carry keywords, syntax-form invocation is supported — written directly according to the constructor parameter pattern:

class Person *[
name: ''
age: 0
new: alias named <String name> aged <Number age> -> {
self.name = name
self.age = age
self
}
]

const alice = new Person named 'Alice' aged 30

Instantiation uses the call forms above; the function-style new Person(...) is not supported.

3. Member Access​

The dot . is used to access class members:

const person = *[ name: 'Alice', age: 30 ]
print person.name .. ' is ' .. person.age .. ' years old'

4. The self Method​

Inside class methods, self refers to the current instance object; the instance itself is accessed through self.

class Counter *[
count: 0
increment: alias -> {
self.count += 1
self
}
getCount: alias -> {
self.count
}
]

const c = new Counter
call c.increment
call c.increment
print (call c.getCount) ~ outputs 2

Zero-argument methods are invoked with call <Object>.<method>.

5. Class Methods​

Methods are defined in the class body:

class Person *[
name: ''
age: 0
greet: alias -> {
'Hello, my name is ' .. self.name
}
new: alias <String name> <Number age> -> {
self.name = name
self.age = age
self
}
]

const person = new Person *[name: 'Bob', age: 25]
print (call person.greet)

6. Class Inheritance​

The extends keyword implements class inheritance:

class Person *[
name: ''
age: 0
greet: alias -> {
'Hello, my name is ' .. self.name
}
new: alias <String name> <Number age> -> {
self.name = name
self.age = age
self
}
]

class Student extends Person *[
studentId: ''
grade: 0
]

const student = new Student *[name: 'Charlie', age: 18]
student.studentId = '12345'
student.grade = 12
print student.name .. ' is a student with ID ' .. student.studentId

Subclasses can override parent class methods:

class Person *[
name: ''
greet: alias -> {
'Hello, my name is ' .. self.name
}
new: alias <String toName> -> {
self.name = toName
self
}
]

class Student extends Person *[
studentId: ''
greet: alias -> {
'Hello, my name is ' .. self.name .. ', and my student ID is ' .. self.studentId
}
]

7. Class Constructors​

Constructors are defined through the new method:

class Person *[
name: ''
age: 0
new: alias <String name> <Number age> -> {
self.name = name
self.age = age
self
}
]

const person = new Person *[name: 'David', age: 35]
print person.name .. ' is ' .. person.age .. ' years old'

Constructor Delegation (super.new)​

A subclass constructor calls the parent class constructor through super.new; after the parent constructor runs, the subclass initialization continues:

class Parent *[
name: ''
new: alias <String n> -> {
self.name = n
self
}
]

class Child extends Parent *[
extra: ''
new: alias <String n> -> {
super.new n
self.extra = '!'
self
}
]

const child = new Child ['bob']
print child.name .. child.extra ~ outputs bob!

8. The super Method​

super can be used inside both class methods and ordinary object methods to invoke an overridden implementation on the parent object; self remains bound to the current instance.

class Animal *[
name: ''
speak: alias -> {
'animal:' .. self.name
}
new: alias <String n> -> {
self.name = n
self
}
]

class Dog extends Animal *[
speak: alias -> {
(call super.speak) .. ' (dog)'
}
]

const dog = new Dog ['Rex']
print (call dog.speak) ~ outputs animal:Rex (dog)
  • self binding: when a parent method is invoked via super, self still points to the child instance and can access the child object's data
  • Multi-level inheritance: in inheritance chains of three or more levels, super walks up one level at a time without recursing
  • Has super: (Has super) returns a boolean inside a method body, indicating whether the receiver's type has a parent — a method inherited from a parent and not overridden also returns true
  • Not enumerable: super never appears in Object.keys results

9. Object Inheritance​

Inheritance in Alia is not limited to classes: any object can inherit through the extends expression, with the syntax <Object> extends <Object>:

const base = *[
speak: alias -> {
'base-speak'
}
]
const dog = *[
speak: alias -> {
(call super.speak) .. '-dog'
}
] extends base

print (call dog.speak) ~ outputs base-speak-dog

class X extends Y *[body] is semantically identical to *[body] extends Y; a class is essentially "object inheritance expression + type creation + const binding".

10. Example Code​

10.1 Basic Class Example​

~ basic class example
class Person *[
name: ''
age: 0
greet: alias -> {
'Hello, my name is ' .. self.name
}
celebrateBirthday: alias -> {
self.age += 1
'Happy birthday! You are now ' .. self.age .. ' years old'
}
new: alias <String name> <Number age> -> {
self.name = name
self.age = age
self
}
]

const person = new Person *[name: 'Alice', age: 30]
print (call person.greet)
print (call person.celebrateBirthday)
print person.age

10.2 Inheritance Example​

~ inheritance example
class Animal *[
name: ''
speak: alias -> {
'Animal sound'
}
new: alias <String name> -> {
self.name = name
self
}
]

class Dog extends Animal *[
breed: ''
speak: alias -> {
'Woof! Woof!'
}
]

class Cat extends Animal *[
breed: ''
speak: alias -> {
'Meow!'
}
]

const dog = new Dog *[name: 'Rex']
dog.breed = 'German Shepherd'
const cat = new Cat *[name: 'Whiskers']
cat.breed = 'Siamese'

print dog.name .. ' says ' .. (call dog.speak)
print cat.name .. ' says ' .. (call cat.speak)

10.3 Complex Class Example​

~ complex class example
class Rectangle *[
width: 0
height: 0
area: alias -> {
self.width * self.height
}
perimeter: alias -> {
2 * (self.width + self.height)
}
isSquare: alias -> {
self.width == self.height
}
new: alias <Number width> <Number height> -> {
self.width = width
self.height = height
self
}
]

class Circle *[
radius: 0
area: alias -> {
3.14159 * self.radius * self.radius
}
circumference: alias -> {
2 * 3.14159 * self.radius
}
new: alias <Number radius> -> {
self.radius = radius
self
}
]

const rect = new Rectangle *[width: 5, height: 10]
print 'Rectangle area: ' .. (call rect.area)
print 'Rectangle perimeter: ' .. (call rect.perimeter)
print 'Is square: ' .. (call rect.isSquare)

const circle = new Circle *[radius: 7]
print 'Circle area: ' .. (call circle.area)
print 'Circle circumference: ' .. (call circle.circumference)

11. Classes and the Type System​

Alia enables the static type system by default; type checking runs in the Check phase, after Astify and before Interpret/Compile. Type annotations can be added to class definitions:

class Person *[
name: String = ''
age: Number = 0
new: alias <String name> <Number age> -> {
self.name = name
self.age = age
self
}
]

const person: Person = new Person *[name: 'Alice', age: 30]

12. Static Class Members​

Static members can be added to class definitions:

class MathUtils *[
static PI: 3.14159
static E: 2.71828
square: alias <Number num> -> {
num * num
}
cube: alias <Number num> -> {
num * num * num
}
]

print 'PI: ' .. MathUtils.PI
print 'E: ' .. MathUtils.E
print 'Square of 5: ' .. (MathUtils.square 5)
print 'Cube of 3: ' .. (MathUtils.cube 3)

Member methods with parameters are invoked in expressions as (<Object>.<method> <args>); zero-argument member methods are invoked with call <Object>.<method>.

Next Steps​