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 [email protected]
Note that posts from new members are moderated - please be patient with your
first post.
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en