> An open question is "Is being purely functional, even excepting I/O, worthwhile?" Or is it, as was suggested to me via email earlier this year, the equivalent of writing a novel without using the letter 'e'?<p>This is a great question. I've been playing with pure functional programming, and I also noticed that applying it to video games is very hard. In other areas, FP provides many advantages such as testability and safe parallel execution. But for simple games these are not that useful. Simple games seem very well suited for the imperative, global state model.<p>There is one aspect that might make FP worth it for games and it's interactive programming. The immediate feedback loop described by Bret Victor [1], with the possibility of going back and forth in time is easier to achieve with a pure functional approach.<p>The Elm debugger [2] is an implementation of this concept, using hot swapping and functional reactive programming it's possible to design games interactively in a very unique way that it's hardly achieved with the traditional imperative approach.<p>[1] <a href="https://www.youtube.com/watch?v=PUv66718DII" rel="nofollow">https://www.youtube.com/watch?v=PUv66718DII</a>
[2] <a href="http://debug.elm-lang.org/" rel="nofollow">http://debug.elm-lang.org/</a>
It seems at least some of the difficulties described are managed better in Haskell.<p>For example, rebinding to the same name (as an alternative to destructive update) can be handled conveniently and nicely via state monad and the lens library, with syntax roughly like:<p><pre><code> pacman.position += speed
</code></pre>
This is still purely functional because the resulting type is roughly: State Game () which is equivalent to: (Game -> Game) (a pure function that takes a game and returns a modified game).
Two links that come to mind:<p>- <a href="https://github.com/ocharles/netwire-classics" rel="nofollow">https://github.com/ocharles/netwire-classics</a><p>- <a href="https://github.com/ekmett/lens/blob/master/examples/Pong.hs" rel="nofollow">https://github.com/ekmett/lens/blob/master/examples/Pong.hs</a>
Maybe it's just me but I find the part about structures a bit silly.
So let's get rid of the big fat structs, and replace them with tuples. Nested tuples. How is that any different than a struct, apart from immutability? And it is worsened by the fact that the fields now are just indexes in a tuple/list instead of being named.
This is my pure-functional Katamari "game" (more of an outline / example, than a full-blown game):<p><a href="https://github.com/fccm/OCamlODE/blob/master/examples/katamari.ml" rel="nofollow">https://github.com/fccm/OCamlODE/blob/master/examples/katama...</a>
This is cool. A lot of people tell us how great functional programming is (I can see the benefits of avoiding state not having side effects). But it is rare to see an article that shows how you go about designing something in that style.
> <i>Back when I actually wrote 8-bit games, much of my code involved updating timers and counters used for animation and special effects and so on. At the time it made a lot of sense, given the limited math capabilities of a 6502. In the modern world you can achieve the same by using a single clock counter that gets incremented each frame.</i><p>So just to demonstrate an IMO elegant approach for the destructive style for updates that depend on current time values: for each update, pretend all computation happens in an instant, specifically: at the moment an event triggered an update (this could be the central game loop, user input, etc). Doing so makes it easy to keep everything synchronised; it's the abstraction Céu uses to great effect for its synchronous concurrency model[0]. The only limitation is that you need to keep your updates small and fast enough to finish before the next update is triggered, but that's a good goal to strive for anyway.<p>Pretending all computation happens instantly let's me use two values: a timestamp referencing to global time, and time difference with the previous time this update was triggered. Counters that get updated every time this update is triggered just increment by time difference. Counters that don't always get updated (or if different events can trigger the update) have to store the timestamp of the last update they were called, compute their own time-difference on the fly with a simple subtraction, update their value and store the new time-stamp. Very simple.<p>Using timers just means checking if the updated value is more than a certain value, and if so subtract that value and call another function. Basically, incremental error algorithms[1], like it is applied in Bresenham's Line Algorithm[2], but for timers instead of slopes.<p>(Note that this could all be written in a functional style with functions that take a current counter value and current/previous timestamp, returning a new counter value, but at that point it just feels like writing functional code imperatively)<p>I often use this in my Arduino code. It speeds up my programs, because calls to <i>millis()</i> are not as cheap as referencing a global, and all we need computatationally with this approach are addition and subtraction, avoiding multiplication, division and modulo. More importantly it makes it much easier to reason about what is happening, because if all updates reference the same time-step, side effects are much more predictable.<p>The aforementioned Céu language really takes this style of concurrency to heart in a very elegant way.<p>[0] <a href="http://www.ceu-lang.org/" rel="nofollow">http://www.ceu-lang.org/</a><p>[1] <a href="https://en.wikipedia.org/wiki/Glossary_of_computer_graphics#incremental_error_algorithm" rel="nofollow">https://en.wikipedia.org/wiki/Glossary_of_computer_graphics#...</a><p>[2] <a href="https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm" rel="nofollow">https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm</a>