Jump to content

Any other functional programmers out there?


ProSteveSmith
 Share

Recommended Posts

Hey all, I was wondering if anyone else was doing functional or functional-ish programming out there. My current game uses RxJs to stream a clock tick and user input into the composed function that is most of my game. I clearly separate side-effects (like generating random numbers) and rendering to the screen into their own functions. (I've open sourced my code for those who are interested).

At the moment the performance could be better, but I'm currently doing a rewrite using ImmutableJS and the results are good so far. I've already started working on a second game and plan to use the functional language Elm (which compiles to JavaScript) and am thinking about trying to use PureScript and possibly even ClosureScript in the future.

How 'bout it, any other functional fans out there?

If you don't know what functional programming is, but would like to know, I could go on about it for hours and hours (could be a good or a bad thing depending on your point of view :) ).

 

 

Link to comment
Share on other sites

I'm fully on the functional bandwagon, its a wonderful way to program.

If you've ever experienced a race condition or problems with synchronising data structures then you'll know how painful they are. Functional is hardly a silver bullet here but if you follow along and do things properly with functional techniques then you rarely run in to these sort of difficult and time-consuming errors.

At its essence functional program aims to eliminate (or reduce) transitional state, as an example, if you have a data structure representing a unit in your rts game then its likely you'll want to use bits and pieces of the full unit data in difference instance i.e. you'll want some of it for displaying an update view, you'll want some different bits of it to display in a tooltip, you'll want very different bits of it for when it attacks another unit.

Functional languages force you to create transformation functions to change that data into what you want, the key here is that they force you to do it when you want it and get rid of it afterwards (JS doesn't force you at the language level but you can still use the theory, JS does have language support for most functional techniques making it a good candidate for writing in a functional style, it was designed this way too, not just a coincidence).

By enforcing transforms to underlying data structures it pushes you down a few key avenues:

* Each view on to the data is independent, they request data when they want it so that it is fresh, there are no syncing issues here, data is represented once (single source of truth) and can be minimally represented as your transforms can add specifics for their use-cases.

* Changes to data are carefully managed (many functional languages enforce this with immutable data structures) so that it can become hard (or even impossible, depending on the language) to have data inconsistencies.

* Race conditions aren't necessarily eliminated but as you're managing changes and actions in a single place its easy to manage things so you never hit a problem caused by it.

You can't really do the above with OOP because OOP involves writing and creating objects (and classes usually) and having all this state hanging around, see the banana-gorilla-jungle explanation.

Some of the most useful functional techniques can be adopted even if you're in an OOP world though.

Creating pure, or referentially transparent, functions is a boon for developers. Pure functions take an input and produce an output based only on their inputs, they effectively do not touch the outside world, they are idempotent, i.e. you know that you will always get the same output given the same input/s. This means you can reason about what that function does, you can understand it fully by taking it in isolation, you don't have to understand the system around it to understand what it does. They become easily testable so any bug or error states can be discovered by automated unit tests but if one does sneak through your tests and you find you need to make changes later then you can do so easily because you know you won't create issues elsewhere in the code. This is absolutely golden.

The downside is of course performance. For certain use-cases you're potentially running the same transforms multiple times when you don't need to i.e. being inefficient. Functional programming likes to do things on demand but games often want to work with an update loop as things are often changing very frequently so sometimes some transients states hanging around reduces load (at the expense of far greater complexity). This is the biggest problem I've faced for fast paced games using these techniques but you might be surprised at how far you can get and there are lots of techniques for improving this stuff without creating more state.

The 2nd biggest problem is that many libraries out there don't want to play nicely with functional techniques, this isn't so much of a problem for the general web app/website world as there are numerous libraries out there for those use cases now but it is a problem for many games-related modules/libraries.

Link to comment
Share on other sites

@mattstyles That's awesome!

@b10b Here's my explanation of how functional programming works and how I use it.

The idea behind functional programming is to have most of your code be pure functions. A pure function is one that returns the same thing every time given the same arguments, just like a mathematical function. A pure function also has no side effects, like printing to the screen or saving to disk.

Here's an example:

const f = x => 2 * x

f(3) will always return 6. f(6) returns 12 every time. This what Matt means by "referentially transparent"; if you wanted you could make a table of arguments and return values instead of actually calling the function.

Also, calling f never causes side effects like modifying the database or opening a network connection.

If most of your program behaves in this predictable, purely functional, way, it's easier (so claim functional enthusiasts like me) to debug and refactor.

Of course your program still needs to have side effects, but if you push that stuff to the edges and keep it out of program logic, the rest of your code becomes much simpler to understand and modify (again, imo).

So how would you write a game this way? Here's my approach:

Think of game state (the position of your player, monsters (their health and positions on the field), pretty much everything your game is keeping track of) as one big data structure called gameState. This is @mattstyles's single source of truth.

const gameState = {
    monsters: [
        /* monster data goes here */
    ],
    player: {
        /* player stats go here */
    },
    //...and whatever else you need to keep track of
}

Then think of player input, as well as the game clock as a big stream of data. Every time something new comes down the stream we call the game function:

const game = (oldGameState, input) => /* a pipeline of functions that return a new game state */

The game function then returns a new game state, which triggers a re-render to the screen, the playing of a sound, or whatever. Replacing old game state with new game state is a side effect, as is rendering to the screen or playing a sound, but that doesn't happen in the game function, the game function stays pure, and thus remains easy to reason about, extend or fix.

One problem with this approach is that the game returns a whole new version of game state every time, which is expensive in terms of memory. But libraries like immutableJS can optimize this for you. Also, languages like Elm, ClojureScript, and PureScript do this kind of optimization at the language level. Like Matt says, it's also possible that you might run into performance issues, this is more true for JavaScript itself than an actual functional language that optimizes at the compiler level. If you run into that kind of problem you can look at whatever functions are your bottleneck and write them in an impure but optimized way.

In my game, there is a constant input stream of clock ticks, commands to create new asteroids, as well as up and down button presses from the player. The game function takes these along with current game state and returns a slightly modified version of game state with the rocket's position changed and/or a new asteroid appearing.

I don't see why this approach to programming wouldn't work for any game genre. I'm starting another game project using Elm that will be a simple rpg battle engine and (possibly) a shop you can use between battles. Any time new input comes in, the game function will respond with a monster attack, the using of a potion to heal a player, or changing game state so the game renders "You Died" to the screen.

Link to comment
Share on other sites

@ProSteveSmith you might be interested in my main side project, https://github.com/mattstyles/raid, its a data management layer that manages transitions from one state to another and the action which create these mutations to the main state. It started out way before redux but as its been simplified its ended up very similar but actually contains state in a stream, despite more computational complexity (of maintaining a stream) perf is nearly identical to redux, initially it was more complex and involved merging several different input streams rather than being a simpler affair using an event emitter and structured messages, the merging of streams is next on my todo list to reintroduce as its a really nice way of constructing an app from the consumers point of view but much more complex for the library, but, thats a good thing, complexity should be hidden behind heavily tested modules so that the app business logic remains firmly stuck in the scope of the app.

Conceptually the library initially directly followed the Elm architecture pretty carefully, its diverged slightly to maintain simplicity but I'd like to bring it a bit closer again.

I've built a couple of PoCs which are games, really nice experience.

Link to comment
Share on other sites

@mattstyles @ProSteveSmith - thank you for the intro into functional programming. It's high on my list of to do's as I heard that it makes you a better programmer overall. One question though:

How adequate it's functional programming for larger projects? How does it compare in terms of time spent and implementation difficulty when compared to its OO/procedural counterpart?

Link to comment
Share on other sites

Hey @scheffgames, quick answer is that most of the techniques you use are more applicable to larger applications, its one of the first things Dan Abramov (creator of Redux and a big speaker on bringing functional techniques to JS and web app development) often mentions when tackling that question.

There is usually a little more initial overhead when employing functional techniques but the savings you get further down the line only become more significant as an application grows in maturity and complexity.

The initial overhead is sometimes more boilerplate (this is solved by the glut of top-quality modules and libraries over the last year or so hitting the ecosytem) but usually in more thought going in to data structures and data flow throughout an application (which is hardly a bad thing). The key decisions here help to structure what your app will look like and decouple various parts of the system you are creating, if done right (and there are lots of good architecture examples out there when you dig in to it) then you'll find your app more resilient, more thoroughly tested and easier to adapt if (when) requirements change down the line.

Employing pure functions (as an example) means you can change that function as you need and its super easy (often trivial) to test, plus, the real kicker, you know what is happening with those functions, you know what goes in and what comes out and so you can not get caught out by making changes and creating bugs in other areas of your code. Functions that touch other areas are your side-effect generating functions and you'll know about those and closely monitor them, preferably employing far more rigid and larger tests to deal with them.

As a quick case study: my current day-to-day is not gaming, its creating a major ecommerce platform, its a client-side (well, isomorphic) application driven by restful apis and after pretty thorough due diligence we hit the functional bandwagon hard. After several hard months and some changes of requirement our app is nice and resilient to those changes, we can develop with agility as our app is highly decoupled. A huge portion of our application is now utility functions, small, composable, pure functions that we then use to create more complex data flows throughout our app. Performance is incredible. So far (we're close to shipping) we've hit no real awkward bugs such as data inconsistencies or race conditions because our architecture makes it hard to slip into those pits. See this great article on the pit of success, our React/Redux stack (with some complimentary stuff) and our architecture choices have meant that we continue to reap the rewards of falling into the pit of success (we toyed with full Elm but too risky for us). There is no reason a game application would not receive the exact same benefits.

Some of the things I mention (such as data consistency and data flow) can be managed in a wholly OOP world too (of course they can, its the predominant programming style for many many years) but its just that functional techniques often end up this way with no additional effort, you fall into that pit of success simply by following the rules, it really feels like your systems are working for you, which is a wonderful situation to be in. It's rare I've thought 'this is a ballache to do with functional stuff', but, when that happens you make a decision on it and employ the right tool for the job (a key skill for developers, well, for anyone), JS supports both styles well (yes, we can bikeshed on truly supporting either but probs not helpful right now!).

The flip side is some things are more difficult, delivering data on demand can be computationally expensive and wasteful, but, on that, I think we're all aware of the dangers of optimising too early, even for something very computationally expensive like your average game (particularly fast moving ones) you might be surprised at how far you can get before perf becomes a problem and you might find some things you're worried about initially never become a problem. Test for performance early and make your decisions based on those tests, two bonuses here, you can measure to find out where you can get the most bang for your buck regarding changes and you can monitor those changes to make sure they're doing what you think (again, these concerns/solutions aren't functional exclusive, you should be doing these no matter how you write code). Moving back to the case-study: we're initially targeting mobile web and I was extremely worried about perf given the metric ton of JS we're running to do anything, but, we monitored early and performance easily exceeds anything I've been involved in previously which is also coupled with development speed—both our app running perf and our development cycle performances are amazing and I wholly contribute this to our initial arch/tech choices.

tl;dr

Functional techniques can often seem overkill or long-winded initially but come into their own as an app grows in complexity, meaning that larger projects benefit more from this approach.

As with all things, do your research, monitor early, and make your decisions based on that. There is no silver bullet, functional or OOP, the good news is, even if you go for an OOP arch you can slip in plenty of functional stuff that can help you and vice versa.

Know your tools and employ wisely!

Link to comment
Share on other sites

Read with interest, thank you.  I'm a user of functional (or functional reactive) and I'm always interested in how others can gist their passion!  I've seen clear benefits going functional with appdev (React + Flux) yet remain very satisfied with SOLID oop for gamedev.  I would summarise as: what takes considerable discipline with oop is (usually) inherent with functional.

Link to comment
Share on other sites

  • 3 months later...

This is a really interesting thread. I recently put together a little POC of a 2D platformer demo using React, Redux, and SVG rendering:

https://github.com/TomWHall/ReactReduxSVG

At the moment all you can do is run and jump around 2 screens. The point was to learn about Redux and functional patterns in general. In my "spare" time I've written a few casual HTML5 games using canvas / WebGL (mostly via PIXI), and they have all ended up feeling very OO, and also too procedural and brittle in the way state is updated.
I wanted to experiment with a derived, declarative UI (React) backed by a functionally managed game state. I'm really impressed by what you can do quite quickly by wiring your state with React-Redux's "connect" function. There are still a few classes with internal state (mostly React components), but they're quite encapsulated,and where possible I'm trying to use pure functions for primary logic.
I used SVG for rendering because I love SVG, though it's just placeholder graphics for now.  

Link to comment
Share on other sites

4 hours ago, Boolean Operations Limited said:

I'm really impressed by what you can do quite quickly by wiring your state with React-Redux's "connect" function. There are still a few classes with internal state (mostly React components), but they're quite encapsulated,and where possible I'm trying to use pure functions for primary logic.

That's awesome, and don't be afraid to have some local state for components, often it makes more sense to put it there. I sometimes think about it by treating the main state object (in this redux, but there are lots of other data libs out there) as a permanent store, i.e, if I refreshed what state would I need to replicate the previous (pre-refresh) state, if I could do without some state, such as whether an input field is focused, then that is a candidate for local component state (unless its trivial to add it to 'global' state).

I checked out your project, worked great (although I got some casing warnings about certain libs, React vs react, which hints that something pretty odd might be going on, stupid webpack), had a quick glance at the code, looks great.

Interesting to see react used to render svg as well, you mention pixi, have you seen react-pixi? It bolts pixi on as a renderer to react so that pixi can respond to react diffs, I tried it a little while ago and it seemed to work well but I'm itching to kick the tires on it more as FP can be a little hard on the performance for frequently changing apps (such as most games). I've been playing recently with different ways to render and update very large tile maps, an interesting challenge to get performant. I created a lib before redux came out (it is very very similar functionally to redux but a slightly different implementation strategy) and have been working to get it more performant alongside, challenging but fun stuff.

Also, I see you're using redux-thunk, thats a great lib but quite limited, if you find you're doing more and more logic, particularly asynchronous data changes, check out redux-saga, its a simply wonderful library. I've been using it for nearly a year now on a complicated ecommerce project with lots and lots of data handling, mostly async, and it keeps getting better and better, a truly incredible library.

Link to comment
Share on other sites

Glad this discussion has happened.

I'm quite interested in functionnal programming, although I always find it quite "artificial" (my lisp courses maybe...)

However, I'm still quite skeptical about functionnal programming performance since, in my mind, and I'm probably wrong, it creates (unecessary) copy of data which can lead to some extra work for the GC (which is a burden in JavaScript...).

Interested to have functionnal programmers feedback on this point, especially when coding video games, where performance is a serious matter. :)

Link to comment
Share on other sites

4 hours ago, yahiko said:

Interested to have functionnal programmers feedback on this point, especially when coding video games, where performance is a serious matter.

Yeah, there isn't much getting around this, small objects are often created frequently if the state changes frequently, as it will do with gaming, and it can be/or is a problem.

If you take the Elm architecture (implemented by Redux if you're familiar with that library) is that state for the entire app is kept in one structure and then it is recreated for every change to the structure. For a situation where your state tree contains hundreds of entries and you just change one boolean somewhere in there a naive approach would be to recreate the entire state tree, even though you're only changing one value, which is very wasteful, however, you might be surprised at how good the JS engine is at compiling such changes to reuse as much as you can at it'll do this underneath JS so you can't (and don't really want to, so long as it working!) touch it. ImmutableJS takes this recreation of state a step further by creating changes that reuse as much of the data structure as it can at the JS level. See uGHcx.png

Another key thing to employ here is memoization, which can be extremely easy with pure functions. Memoization is the process of remembering previous operations (often just the last one) and just returning the memoized value if inputs are the same, this means there is just an equality check to hit perf which is super cheap. Libraries like reselect implement this to great effect.

Of course, you don't have to stick with FP techniques for everything, for example, you might choose to use it for your slower-paced UI elements and have your main game camera/screen using other techniques. I imagine that might get annoying but it would certainly work well.

Link to comment
Share on other sites

  • 10 months later...

I've finally updated my ReactReduxSVG demo - to v0.2.0 ☺️

Repo: https://github.com/TomWHall/ReactReduxSVG

Demo: https://tomwhall.github.io/ReactReduxSVG/

It now has somewhat more advanced physics (still plenty of bugs there!) and an example of an object which the player can interact with (elevators). I also restructured the code a lot.

@mattstyles the state graphic above is interesting. Is this the default behaviour in Immutable.js? The way I handle the above type of update is basically the same as shown in the "Quick usage" example of this repo:

https://github.com/mariocasciaro/object-path-immutable#quick-usage

That is, in the graphic above I would end up with the whole right-hand side of the updated tree being red, not green - i.e. if anything changes in the tree I update the top-level object and everything down the path to the changed node.

This seems like the safest way to me, so you could map any level of the object to a React component and potentially implement an equality check before re-rendering (which I'm not currently doing anyway, I'm just letting React do its default re-render).

But I feel like I still have a lot to learn about Redux patterns so my whole approach could be full of holes ?

 

 

 

 

 

 

Link to comment
Share on other sites

  • 1 month later...

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...