Reputation: 1513
I have a game state represented as a map and some logic that updates that state on every game 'tic'. But I can't figure out how to structure the update function in any sane way.
What is the idiomatic pattern for structuring functions like this?
Here is some pseudo code for what I want to do:
(defn tic [g] "Return an updated game"
g1 = (update-in g [:day] inc)
g2 = (if (some-cond) (some-update-func g1) g1)
g3 = (update-in g2 [:fu] fu-update)
... many more ...
g-last)
I don't really care about the intermediate states, but using the -> macro doesn't work (since there are some conditionals).
A hack that works is using a local atom that is reset! for every 'line' in the update function. But that can't be how it's supposed to be done?!
Upvotes: 3
Views: 176
Reputation: 921
You can still use ->
if for the conditional steps, you wrap the operation in an anonymous function.
(-> g0
...
(#(if (some-cond) (u %) %))
...)
If you are concerned about efficiency (you mentioned this is a game) I would recommend you use cond->
or maybe create your own macro. cond->
requires repeating true
for expressions that are always threaded, which may be tedious, depending on the number of items in your thread.
Here is a macro that can be used in conjuction with ->
that avoids excessive creation of anonymous functions and avoids the repetitiveness of cond->
:
(defmacro maybe [val sym cond expr]
`(let [~sym ~val] (if ~cond ~expr ~sym)))
It can be used like this:
(-> g0
...
(maybe gn (some-cond gn) (updater gn))
...)
If you don't need to use the partially-processed value gn
in the condition expression, you can just use cond->
instead of maybe
:
(-> g0
...
(cond-> (some-cond g0) updater)
...)
Another example:
(-> 10 (maybe gn (= gn 10) (* gn 100)))
Which evaluates to 1000
Upvotes: 3
Reputation: 8593
I would suggest to extract each of the steps in a nicely named function, so that you can use ->. Pseudo code:
(defn tic [g]
(-> g
inc-day
random-weather
grow-trees
...))
For any conditional logic, you can just do something similar to what you do in your g2 step.
Perhaps you will find synthread lib useful. I found this video very instructive.
Look also at cond-> to see how could you mix -> with some cond. For example your cond could look like:
(cond-> g
true (update-in [:day] inc)
(some-cond) some-update-fund
true (update-in [:fu] fu-update))
Upvotes: 6