I'm implementing a litte "game" thing in Clojure. So far I'm passing around a "world status" object among functions. It is very "functional" and I can simulate any moment of the game by simply feeding the system a made-up world state.
Since Clojure has a very sophisticate system for managing state (references, atoms...) I wanted to know what is the more idiomatic way of programming Clojure, whether to use its system or to stick to a more functional approach.
Thanks.
EDIT:
This is an interesting read I've just found describing more or less the pattern I'm using.
Pure functions are already idiomatic Clojure. The reference types (ref, atom, agent) are there to coordinate shared state.
As long as nothing is shared (you aren't updating the world in one thread while rendering in another, or coordinating multiple players on their own threads) there is no reason to share state, and thus no reason to break from purely-functional style.
Another exception would be to optimize for performance: but when performance can be enhanced by mutability you want to keep the mutations as local as possible. This is where transients or Java arrays come in. The functions that wrap these mutable optimizations are usually still pure functions.
Clojure is a functional programming language designed to take advantage of multi-core/SMP processors. You can get a lot out of a functional programming language without shared memory access, and indeed Erlang does this wonderfully, but it does not take advantage of all the processors powers.
Where clojure shines in comparison to 'Actor model' languages is when multiple threads want to work on the same data in a meaningful and coordinated way. If you have a trivially parallelizable problem like image processing then you don't need these advantages and you can just send one chunk of data to each worker. When these bits of data are interdependent then Coordinated shared access becomes a real advantage.
In the context of games you can use this to have many threads updating the game world and one thread showing it to the user. a ref
will ensure that the user always sees a consistent game world, while many threads are editing it. without this you would need to have each thread responsible for determining when to show it to the user, or only have on thread.
Another reason to use such a model for editing a game world, other than speed, is to allow you to seperate processes that do different things into different threads in one of Rich's early clojure examples he shows an ant simulator where each ant has its own thread that updated the ants position on the board, which made for some very short and simple code.
Writing the game in the current functional style is already the right way, so long as only one thread or context is writing to that data (which is what happens when you recur). Really, the key advantage to those facilities you mentioned is when dealing with shared state. If you want to parallelize your program with multithreading, then leveraging those tools might be useful.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With