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
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,selfstill points to the child instance and can access the child object's data - Multi-level inheritance: in inheritance chains of three or more levels,
superwalks 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 returnstrue - Not enumerable:
supernever appears inObject.keysresults
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>.