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.

Reply via email to