I couldn't resist writing some more macros! (defmacro << [k-or-ks f] `(fn [state#] (update-in state# ~(if (vector? k-or-ks) k-or-ks (vector k-or-ks)) ~f)))
(defn >>* [state form] (condp = (first form) '>>> `(fn [~state] ~@(rest form) ~state) '<<< `(fn [~state] ~@(rest form)) form)) (defmacro >> [state & forms] (reduce #(list (if (#{'<< 'fn 'fn* 'clojure.core/fn 'clojure.core/fn*} (first %2)) %2 `(fn [s#] ~%2 s#)) %) (conj (map (partial >>* state) forms) state))) With those the resulting code looks pretty clean and remain purely functional: (next-phase [state] (>> state (stop-timer) (<< :phases #(conj (vec (rest %)) (first %))) (>>> (log :info "change phase to %s" (-> state :phases first key))) (<<< (start-phase state)))) Now, I'd need to find better names! On Sunday, April 15, 2012 11:25:21 PM UTC-4, Nicolas Buduroi wrote: > > I'm working on a turn-based game and I'm looking for a good way to manage > states. In the game each turn is composed of multiple phases. I started by > using atoms for the phases field (this is a sequence of functions) in a > record and realized that it wouldn't be ideal to keep track of states in > the case where I'd need to keep a snapshot of every phases. Here's the > original code I had: > > (defrecord Game [phases] > (next-phase [this] > (stop-timer) > (swap! phases #(conj (vec (rest %)) (first %))) > (log :info "change phase to %s" (key (first @phases))) > (start-phase this)) > > I then started to think that this would be a good opportunity to use a > state monad. I've tried to reimplement the above code using the algo.monads > library but the result was less than satisfactory (probably due to my own > shortcoming), here's the monadic version: > > (defrecord Game [phases] > > (next-phase [this] > (-> > ((domonad state-m > [_ (fn [s] (stop-timer) [s s]) > _ (update-state > (fn [s] > (update-in s [:phases] > #(conj (vec (rest %)) (first %))))) > _ (fn [s] > (log :info "change phase to %s" (key (first (:phases s)))) [s > s])] > nil) > state) > second > start-phase)) > > As my code probably doesn't need the full power of the state monad, I > tried to write a lighter-weight version using the following macro: > > (defmacro >> [& state-and-forms] > (reduce #(list (if ('#{fn fn*} (first %2)) > %2 > `(fn [s#] ~%2 s#)) %) > state-and-forms)) > > Which let me write: > > (next-phase [state] > (>> state > (stop-timer) > (fn [s] (update-in s [:phases] #(conj (vec (rest %)) (first %)))) > #(do (log :info "change phase to %s" (key (first (:phases %)))) %) > #(start-phase %))) > > With some more helper macro this version looks promising. In the end I > wonder if there's some Clojure feature I'm overlooking or if I should > rethink the whole solution? Is there a better way to accomplish this? > > > -- 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