I'm a little late here, but I wanted to point out that there is a pretty
good match between what you're describing as an FSM and Akka actors. In
case you're still looking into this, you might want to check out Okku, my
(very thin) Clojure wrapper for Akka.

On Sunday, November 18, 2012, timc wrote:

> 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<javascript:_e({}, 'cvml', 
> '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 <javascript:_e({}, 'cvml',
> 'clojure%2bunsubscr...@googlegroups.com');>
> For more options, visit this group at
> http://groups.google.com/group/clojure?hl=en

-- 
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