Hello - I would appreciate some advice on how to implement something 
equivalent to sub-classing.

The context for this is a finite state machine (FSM), by which I mean a 
thread that waits on a queue of events, then deals with each event (one at 
a time) according to the state it is in, possibly changing state in the 
process.

In Java, an (abstract) super class might be defined for all FSMs, which 
contains common fields such as the current state, timer ID, event queue, 
etc, together with methods for getting and setting the state, starting and 
stopping timers, handling timeouts (this inserts a timeout event into the 
event queue), putting an event into the queue and waiting on the next event 
in the queue. The method to insert an event is generally public so that 
other objects may insert events.

Then, a particular FSM is written as a sub-class; this comprises code to 
execute before the first event is waited for, a loop which waits for then 
processes each event, then code that is executed when the FSM is exited for 
the last time.
The code that implements all the above naturally makes use of the 
super-class methods and fields, since they are within the scope of the 
sub-class.    

It is easy enough to implement all the above as closure of the form

  (defn fsm []
    "Start a thread that is a finite state machine.
     Return [putEventF stopF] where putEventF[ev] is a function that adds 
an event to the event queue,
     and stopF[] is a function that stops the state machine.
     Events are vectors of the form [kind & data]." 
    (let [eventQ (java.util.concurrent.LinkedBlockingQueue.)
          putEventF (fn [ev] (.put eventQ ev))
          stopF (fn [] (putEventF [:stop]))
          state (atom nil)
          going (atom true)
          timerID (atom nil)
          timerExpired (fn [id data]
                         (when (and @timerID (= id timerID))
                           (.put eventQ [:timeout data]))) 
          stopTimer (fn []
                      (when @timerID
                        (extStopTimer @timerID)
                        (reset! timerID nil))) 
          startTimer (fn [period data]
                       (stopTimer)
                       (reset! timerID (extStartTimer period timerExpired 
data)))
          nextState #(reset! state %)]
      (inThread
        (do
          (nextState :init)
          ; Starting stuff ...
          (print "started"))
        (while @going
          (let [[kind & data :as ev] (.get eventQ)]
            (cond = @state
                  :init (do
                          ; Initialization stuff ...
                          ) 
                  :state1 (do
                            ; state 1 stuff ...
                            )
                  :state2 (do
                            ; state 2 stuff ...
                            )
                  ; etc...
                  :stop (do
                          ; Stopping stuff ...
                          (reset! going false)) 
                  (error "bad state"))))
        (do
          ; Exit stuff ...
          (print "terminated")))
      [putEventF stopF]))

where extFunction... are externally defined functions (implementing timers 
etc) and inThread is a macro that executes a form in a newly created thread.
Within the body of the FSM, the code can of course directly refer to all 
the things defined in the let by name.

The problem is that I have numerous FSMs and it's really tedious to repeat 
all the stuff in the initial let of the closure.

One solution is to define a *context* that contains the values defined in 
the closure (as a map with keys instead of the explicit names shown above), 
created by a context factory function.
Then the code has to refer to all the things via a lookup in the context 
map - quite ugly and also tedious.

Another option is that the context factory creates a closure, then returns 
a vector of all the functions defined in the let binding, which can then be 
used directly by the FSM code - that's even more ugly and error prone.

So - the question is - what about an anaphoric macro that creates the 
closure, so that the FSM code can still refer to all that stuff by name?
If this is the way to go, please an example of the syntax (say the 
timerExpired function). The macro syntax really is confusing to me :(

Or, I imagine a macro called myFsmMacro that takes a map as parameter and 
has the same effect as the fsm function above, something like this:

(def myFsmDefinition  {
   :preamble (do
               (nextState :init)
               (print "started"))
   :states {:init (do
                    (initData 1 2 3) 
                    (putEvent :dummy)
                    (nextState :state1)) 
            :state1 (do
                      (startTimer 10)
                      (nextState :state2))
            :state2 (condp = kind
                      :timeout ;....
                      )
            .... all the other states
            :stop (do
                    (reset! going false)) 
            }
   :badState (error "bad state")
   :postAmble (do
                ; Exit stuff ...
                (print "terminated"))
   })

(myFsmMacro myFsmDefinition) --> [putEventF stopF]

Is this feasible? Not as it stands - the map would be full of undefined 
things.

Or, is this an appropriate application of mutimethods (one method per 
state)?

Or, define myFsmDefinition as text, parse it, create form and then eval it?

I don't fully understand Monads, could this be an application of the state 
monad? Or does the state have to be explicitly passed around just like the 
context I mentioned above?
And would it make those local names visible to the FSM code? Or some new 
kind of monad?

Any comments would be appreciated.

Regards, Tim    


-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en

Reply via email to