Thanks for posting! I missed the original posting, but this pattern looks handy. Since you asked for feedback I decided to read through and give some thoughts.
There's a couple of other Nim projects you might be interested in and could give you insights: 1. <https://github.com/mratsim/Synthesis> 2. <https://github.com/paranim/pararules> Your code seems reasonable but still very C-like. Not a bad thing, but you do miss a fair bit of chances for better typing! One quick aside first. You can use your `<--` `>--` macros as prefix operators, which might be easier to read: >-- Message(...) <-- Message(...) Run The big thing I see is that while your `StageMachine` type is inheritable, you don't take advantage of it. For inheritable objects Nim lets you do a safe runtime conversion of types, but it works best with `ref object`. Also, `ref` offers a lot of advantages over raw `ptr`. For example you have the types: type StageMachine* {.inheritable.} = object type WorkerMachine = object of StageMachine ListenMachine = object of StageMachine Run For example if you change them to `ref object` and `ref object of StageMachine` types you can then get ride of the `ptr StageMachine` in the rest of your code. It looks like 90% of your usages are `ptr` types so it'd be cleaner. Then you can do nice things like: import strformat type StageMachine* {.inheritable.} = ref object stage*: string OtherObj* {.inheritable.} = ref object name*: string type WorkerMachine* = ref object of StageMachine wmId*: int ListenMachine* = ref object of StageMachine lmId*: int EnterFunc = proc(me: StageMachine) var cb: EnterFunc proc addProc[T: StageMachine](foo: proc(me: T)) = ## it's easiest to just wrap it but you could create a macro ## or template that did this inside foo without overhead cb = proc(me: StageMachine) = if me of T: foo(T(me)) else: echo "warning: skipping wrong type" ## Example Usage proc receptionInitEnter(self: ListenMachine) = echo fmt"reception: enter => {self.lmId=} {self.stage=}" let lm = ListenMachine(stage: "b", lmId: 456) wm = WorkerMachine(stage: "b", wmId: 123) addProc(receptionInitEnter) proc runProc[T: StageMachine](me: T) = cb(me) ## works let m1: StageMachine = lm runProc(m1) # => "reception: enter => self.lmId=456 self.stage=b" ## allowed but just prints warning let m2: StageMachine = wm runProc(m2) # => "warning: skipping wrong type" ## the compiler prevents this let nonMachine: OtherObj = OtherObj(name: "non-esm") assert not compiles(runProc(nonMachine)) Run You could also do things like make `StageMachine` generic or to use variant types, etc.
