Traits
History Key
- New content
Removed content
Recent Versions
Choose two versions to compare, or click the link to view it.
I became aware of traits through fortress, and a mention on Jon Cowan’s blog. The original papers are available here.
Kin’s implementation of traits is based partly on the original pattern of specifying a set of behaviours and interface requirements, tempered by experience implementing a stripped-down traits based component system in another modelling language.
Traits act by specifying a set of required properties and a set of defined operations on those properties. As such, they are used both as interface and implementation mechanism in kin. The one thing a trait cannot do is define a property value; properties are only defined on concrete types.
Kin allows traits and types to be parametrised with the names of other types.
Explicit Traits
For example, the sequence trait is a parametrised type:
trait seq[T]
req head : T
req tail : seq[T] or nil
def len ()
=> 1 + tail.len()
This defines a trait called seq, which has one type parameter T. Realisations of the trait have to supply two properties, head of type T, and tail which is either a sequence of the same type, or nil, the type of the empty sequence.
The trait also defines a len method, which returns the length of the sequence. This method is defined in terms of the required properties of the trait, so the implementation can be applied to all types which realise the trait. The type defines its own length method def len()=>0 so the recursive operation terminates when the end of the sequence is reached. Other types which realise the seq trait, such as mutable_array may override the len() method defined in the trait to supply a more efficient implementation.
Implicit Traits
An explicit trait must be specified by the type which realises it:
type cons(head:any,tail:cons or nil) : seq[any]
By contrast, an implicit trait can be applied to anything which satisfies its requirements:
trait @implicit stack[T]
req head : T
req tail : stack[T] or nil
req operator::(T,self) : self
req self(head:T, tail:self):stack[T]
def push(val:T) => val :: self
def top() => self.head
def pop() => self.tail
This defines an implicit stack type. When self is used as a type, it means the type of the realisation of the trait. The types nil and cons can be coerced to a stack, as they supply the required properties:
let st = stack([])
st.top() # fails
let st1 = st.push("red").push("lorry").push("yellow").push("lorry")
out <- st1.top() # lorry
out <- st1.pop().top() # yellow
To behave more like a stack in an OO program, you need to create an actor rather than a value type (see ActorStackImplementation).
Enumerated Traits
A trait may be defined by an enumerated set of different traits or types
trait colour = red or yellow or pink or green or orange or purple or blue This means that the type name colour can be used to refer to any of the enumerated types, and no others.
Enumerated and implicit traits may be used with extension methods.
Extension methods
A method may be locally added to a trait using a method which has self as an parameter name. This method definition can be used by any code which imports the definition, or imports all definitions from the module which it is defined in.
module tests::extension_method
def print_to (self, out)
out <- "I'm a " <- self <- endl
type foo ()
def print_to (self, out)
out <- "But I'm a foo!" <- endl
def main (in,out,args)
1.print_to(out)
#=I'm a 1
foo().print_to(out)
#=But I'm a foo!Later:
TODO: mechanism for composition using delegation rather than inheritance.
module examples::trait_composition
trait mobile
req move_to(goal,priority)
trait observable[RadiationType]
req direct_signature(direction):signature[RadiationType]
trait RadiationType := RadioReflective or RadioEmissive or VisibleLight or InfraRed or Sonic or SubSonic or UltraSonic
type simple_radar_footprint (effective_radius) : observable[RadioReflective]
def direct_signature(direction) => spherical_radar_signature(effective_radius)
type simple_kinematic : mobile
...
type armoured_personnel_carrier (@composite radar_signature:observable[RadioReflective], @composite mobility:mobile)
...