traitor

Source   Edit  

This module implements a simple interface over dynamic dispatched traits. It allows one to define the required implementation for a type to match both at runtime and compile time. Enabling the writing of code that does not require inheritance, but still has dynamic dispatch.Defining -d:traitorNiceNames can be used to make the generate procedures have nicer names for debugging.

Types

AnyTraitor[Traits] = StaticTraitor[Traits] or Traitor[Traits]
Allows writing a procedure that operates on both static and runtime. Source   Edit  
Atom = distinct void
Default field name to be replaced for all Traits. As it derives from void it never can be instantiated. Source   Edit  
GenericType = concept typeof(F)
    isGeneric(F)
Cannot instantiate it so it's just checked it's a type T[...] = distinct tuple Source   Edit  
StaticTraitor[Traits] = concept st
    st.toTrait(Traits) is Traitor[Traits]
Allows generic dispatch on types that fit traits Source   Edit  
Traitor[Traits] = ref object of RootObj
  vtable*: typeof(emitTupleType(Traits))
Base Trait object used to ecapsulate the vtable Source   Edit  
TraitType = concept typeof(F)
    for field in default(distinctBase(F)).fields:
      when field is (proc):
        field.paramTypeAt(0) is Atom
        atomCount(typeof(field)) == 1
      else:
        for child in field.fields:
          child.paramTypeAt(0) is Atom
          atomCount(typeof(child)) == 1
    distinctBase(F) is tuple
Forces tuples to only have procs that have Atom inside first param Source   Edit  
TypedTraitor[T; Traits] {.final, acyclic.} = ref object of Traitor[Traits]
  data*: T
Typed Trait object has a known data type and can be unpacked Source   Edit  

Procs

proc getData[T; Traits](tratr: Traitor[Traits]; _: typedesc[T]): var T
Converts tratr to TypedTrait[T, Traits] then access data

Example:

type
  MyTrait = distinct tuple[doThing: proc(_: Atom){.nimcall.}]
  MyType = object
    x: int
implTrait MyTrait
proc doThing(typ: MyType) = discard
let traitObj = MyType(x: 100).toTrait MyTrait
assert traitObj.getData(MyType) == TypedTraitor[MyType, MyTrait](traitObj).data
Source   Edit  
proc isGeneric(t: typedesc): bool
Source   Edit  

Macros

macro emitTupleType(trait: typedesc): untyped
Exported just to get around generic binding issue Source   Edit  
macro joinTraits(traits: varargs[typed]): untyped
Source   Edit  

Templates

template emitConverter(T: typedesc; trait: typedesc[ValidTraitor])
Emits a converter from T to Traitor[trait] This allows skipping of val.toTrait(trait) Source   Edit  
template implTrait(trait: typedesc[ValidTraitor])
Emits the vtable for the given trait and a procedure for types to convert to trait. It is checked that trait is only implemented once so repeated calls error.

Example:

type MyTrait = distinct tuple[bleh: proc(_: Atom, _: int) {.nimcall.}]
implTrait MyTrait
Source   Edit  
template setProc[T, Trait](traitor: TypedTraitor[T, Trait]; name: untyped;
                           prc: proc)
Allows one to override the vtable for a specific instance Source   Edit