Lily provides classes to group together a set of data and related functions. Classes are limited to single inheritance (only one parent) to keep them simple.
class Point(x: Integer, y: Integer)
{
public var @x = x
public var @y = y
public define increase(x_value: Integer, y_value: Integer)
{
@x += x_value
@y += y_value
}
}
The body of a class is where class variables are declared and is also the constructor.
Inside of a class, properties require the @
prefix, but methods do not.
Outside of a class, both methods and properties are accessed through a dot.
Because the dot provides dual access, methods and properties must be unique to
each other.
A class method or property must always start with a scope that is either
private
, protected
, or public
.
Every class property and method must have a scope. Three scoped are supported.
public
fields are available outside of the class, in a parent class, and the
class itself.
protected
fields are available in a parent class, and the class itself.
private
fields are only available in the class itself.
In the above example, constructor parameters are turned directly into class properties. In such cases, the following shorthand is available.
class Point(public var @x: Integer,
public var @y: Integer)
{
public define increase(x_value: Integer, y_value: Integer)
{
@x += x_value
@y += y_value
}
}
Classes support single inheritance, denoted by <
. A user-defined class can
inherit from any native class. The only predefined classes that are native
classes are Exception
and classes that inherit from it.
class Point2D(public var @x: Integer,
public var @y: Integer)
{
public define increase_xy(x_move: Integer,
y_move: Integer)
{
@x += x_move
@y += y_move
}
}
class Point3D(x: Integer,
y: Integer,
public var @z: Integer) < Point2D(x, y)
{
public define increase_xyz(x_move: Integer,
y_move: Integer,
z_move: Integer)
{
increase_xy(x_move, y_move)
@z += z_move
}
}
var p3 = Point3D(10, 200, 3000)
p3.increase_xyz(1, 2, 3)
print(p3.x) # 11
print(p3.y) # 202
print(p3.z) # 3003
Suppose a base class has a method that would be useful in a chain with a parent
class method. Class methods can return self
to allow that.
class Launcher
{
protected var @values: List[Integer] = []
private var @error = false
public define add_value(v: String): self
{
if @error: {
return self
}
with v.parse_i() as Some(s): {
@values.push(s)
else:
@error = true
}
}
}
class Runner < Launcher
{
public define get_values: List[Integer]
{
return @values
}
}
var r = Runner()
var result = r.add_value("1")
.add_value("2")
.get_values()
print(result) # [1, 2]
Class methods receive an implicit self
as their first parameter. If the
static
qualifier is used, no self
will be sent. static
must always be used
after the scope.
class Utils
{
#[
# Invalid: static before scope.
static public define invalid
{
return x * x
}
]#
public static define square(x: Integer): Integer
{
return x * x
}
}
var v = Utils.square(10)
print(v) # 100
Classes are allowed to forward declare methods. While a forward definition is open, parameters cannot be defined.
class Example
{
public var @value = 1
forward protected define second(Integer) { ... }
public define first(x: Integer)
{
if x == 0: {
return
else:
@value *= 2
second(x - 1)
}
}
protected define second(x: Integer)
{
if x == 0: {
return
else:
@value *= 2
second(x - 1)
}
}
}
var v = Example()
v.first(5)
print(v.value) # 32
It is also possible to forward declare a class. There are no restrictions on declarations while there are unresolved forward classes.
forward class Entry { ... }
class EntryPool
{
public var @entries: List[Entry] = []
public define register(e: Entry)
{
@entries.push(e)
}
}
var p = EntryPool()
class Entry
{
public var @values: List[Integer] = []
public define add_value(v: Integer): self
{
@values.push(v)
}
public define finish
{
p.register(self)
}
}
# In another file, after importing EntryPool.
var e1 = Entry()
.add_value(1)
.add_value(10)
.add_value(100)
.finish()
var e2 = Entry()
.add_value(1000)
.add_value(10000)
.add_value(100000)
.finish()
var v = p.entries
.map(|m| m.values.fold(0, (|a, b| a + b )))
.fold(0, (|a, b| a + b ))
print(v) # 111111