First-pass implementation. LaiRu 0.1 covers a solid compatibility slice of the Lasso 9 type system. Some edge cases around trait composition, exact data-member accessor naming, and historical dispatch precedence remain future work.

Defining Types

Custom types can be defined either with type_define or using the richer define … => type { … } body form.

// simple declaration
type_define('Person')
type_define('Employee', -parent='Person')
type_define('Named', -traits=array('Printable'))

// full type body
define Person => type {
  data public name::string = 'Ada'
  data protected role::string = 'dev'
  public summary()::string => .name + ' (' + .role + ')'
}

Member Methods

Member methods are defined with the TypeName->method syntax. Inside a member body, #self refers to the receiver, and .field is shorthand for #self->field.

define Person->greet => 'Hello, I am ' + .name
define Person->score(value) => #value + 1
define Person->score(left, right) => #left + #right

local(p) = Person(-name='Ada', -role='dev')
#p->greet
#p->score(10)
#p->score(3, 4)

Constructors

The onCreate method is called when an object is created via its type name. Data fields declared in the type body can be passed as keyword arguments:

define Person->onCreate(name) => {
  .name = #name
}

local(p) = Person('Ada')
#p->name

Operator methods

define Person->+(suffix) => .name + #suffix

local(p) = Person(-name='Ada')
#p + '!'    // 'Ada!'

Traits

Traits define reusable interface contracts. They can declare requirements (methods the concrete type must provide) and provided methods (default implementations).

define TraitReadable => trait {
  require get(index::integer)::string
  provide first()::string => .get(1)
}

define TraitNamed => trait {
  import TraitReadable
  provide label()::string => .first
}

A type imports a trait using the trait { import … } section inside its body, or via trait_assign:

define Widget => type {
  trait {
    import TraitReadable, TraitNamed
  }
  data public name::string
  public get(index::integer)::string => .name
}

local(w) = Widget(-name='button')
#w->isA('TraitNamed')    // true
#w->label                // 'button'

Parent Types and Inheritance

define Animal => type {
  data public name::string
  public speak => 'I am ' + .name
}

define Dog => type {
  parent Animal
  public speak => inherited->speak + ' and I woof'
}

local(d) = Dog(-name='Rex')
#d->speak    // 'I am Rex and I woof'
#d->isA('Animal')    // true

The inherited->method form (also written ..method inside a member body) calls the nearest ancestor’s implementation.

Data Fields

Fields declared with data generate getter and setter methods automatically. Access modifiers control who can read or write a field:

define Account => type {
  data public  username::string
  data protected balance::decimal = 0.0
  data private  secret::string
}
ModifierReadWrite
publicAnyoneAnyone
protectedSame type / subtype membersSame type / subtype members
privateSame type members onlySame type members only

Direct member assignment such as obj->field = value dispatches to a visible field= setter before falling back to the generated slot write. Slot writes enforce declared type constraints.

Object Identity and Copy

Custom objects are reference-backed: assigning #b = #a makes both variables point to the same object. To get an independent copy use copy or clone:

local(a) = Person(-name='Ada')
local(b) = #a->copy      // independent structural copy
#b->name = 'Charles'
#a->name    // still 'Ada'

object_serialize / object_deserialize provide a value-graph envelope for round-tripping custom objects, preserving shared identity and cycles inside the graph.

Type Inspection

local(p) = Person(-name='Ada')
#p->type          // 'Person'
#p->isA('Person') // true
#p->isA('Animal') // false (unless Person inherits Animal)
#p->listMethods   // list of visible method signatures
#p->hasMethod('greet')  // true / false