Immutable game object, the main issue of functional programming

I try to "learn more" and "learn from" functional programming, and the idea of ​​immutability is good for concurrency, etc.

As a mental exercise, I imagined a simple game in which a character like Mario-esque can run and jump with enemies shooting at him ...

Then I tried to imagine that it was written functionally using immutable objects.

This raised some questions that puzzled me (as an imperative OO programmer).

1) If my little guy is at position x10, y100 moves 1 unit to the right, do I just recreate it using his old values ​​from +1 to his position x (e.g. x11, y100)?

2) (If my first assumption is correct) If my input stream moves the right guy 1 unit and my enemy thread AI shoots the little guy and the enemy thread allows before input-thread, then my guy will lose health, and then when the input is allowed threads will return it and move it to the right ..

Does this mean that I can’t shoot - and forget my flows even with immutability? Do I need to send my threads to do my work, and then a new () little guy synchronously when I have the results of both threaded operations? or is there a simple “functional” solution?

This is a slightly different threading problem than I face day after day. Usually I have to decide if I worry about which order flows are allowed or not. Where, as in the aforementioned case, I technically do not care if it deals damage or moves first. But I do not care if the race conditions during the creation of the instance lead to a complete loss of these threads.

3) (Again, if my first guess is correct). Constantly created object instances (like Mario) have terrible overhead, which makes it a very serious / important design decision?

EDIT Sorry for this extra editing, I was not that good practice here, about the following questions ...

4) If immutability is what I should strive for and even jump, although the hoops of creating new versions of objects that have changed ... And if I create an instance of my boyfriend every time he moves (only with a different position), me the exact same problems as if it were volatile? is that something that referred to it at some point in time really looks at old values? .. The more I delve into it, the more my head rotates, creating new versions of the same thing with different by values, it just seems volatile, via hack: ¬

I think my question is: how much should it work? and how beneficial has it just changed its position?

for(ever)//simplified game-loop update or "tick" method { if(Keyboard.IsDown(Key.Right) guy = new Guy(guy){location = new Point(guy.Location.x +1, guy.Location.y)}; } 

Also confusing: the above code means the guy is volatile! (even if its properties are absent)

4.5) Is this possible with a completely unchanged guy?

Thanks,

J.

+6
immutability functional-programming
source share
6 answers

1) If my little guy is at position x10, y100 moves to the right 1 unit, will I just repeat using it with the old value from +1 to his position x (for example, x11, y100)?

Well, not necessarily. You can instantiate a guy once and change his position during the game. You can model this with agents. A guy is an agent, just like an AI, just like a rendering stream, just like a user.

When the AI ​​shoots a guy, he sends him a message when the user presses the arrow key, which sends another message, etc.

  let guyAgent (guy, position, health) = let messages = receiveMessages() let (newPosition, newHealth) = process(messages) sendMessage(renderer, (guy, newPosition, newHealth)) guyAgent (guy, newPosition, newHealth) 

“Everything” is now unchanged (in fact, under the hood, the line for dispersing the agent probably has some mutable state).

4) If immutability is something that I should strive and even jump, although the hoops of creating new versions are objects that have changed ... And if I create an instance of my boyfriend every time he moves (only with a different position) I don’t exactly the same problems as mine if it were changeable?

Well yes. A cycle with mutable values ​​and repeating with immutable is equivalent.

Edit:

  • For agents, a wiki is always useful.
  • Luca Bolognese has an F # agent implementation .
  • This book (called by some Intelligent Agent Book), although aimed at AI applications (instead of having a technical SW point) is excellent.
+2
source share

Read the Purely Functional Retrogames series, which describes how to implement Pac-Man, using mostly the pure functional language Erlang. The problems that he describes there are common problems for all functional programming games, and he presents good, usable solutions to all of your questions above.

+7
source share

A couple of comments on your points:

1) Yes, maybe. To reduce overhead, practical design is likely to end up sharing many states between these instances. For example, maybe your little guy has an Equipment structure that is also immutable. The new copy and the old copy can safely refer to the same "equipment" structure, since it is unchanged; so you only need to copy the link, not all. This is a common advantage that you get only through immutability - if the "equipment" was changed, you could not share the link, because if it changes, your "old" version will also change.

2) In the game, the most practical solution to this problem is likely to be a global "clock", and such processing will occur once with a clock frequency. Note that your exact scenario will still be a problem if you don't write it in a functional style: suppose that H0 is health at time T. If you passed H0 to a function that made a health decision at time T, you took damage at time T + 1, and then the function returned at time T + 5, it may have made the wrong decision based on your current state of health.

3) In a language that encourages functional programming, instantiating an object is often done as cheaply as possible. I know that on the JVM, creating small objects on the heap is so fast that you rarely have to consider performance in any practical situation at all, but in C # I never came across a situation in which this was a problem.

+3
source share

If everything in the global state of the system, outside the current frame of the stack, is immutable, unless another thread refers to something on the stack ( VERY DANGEROUS ), then there will be no way for the thread to do anything to influence each other friend. You can shoot and forget, or just not bother, shoot first, and the effect will be the same.

Assuming there are some parts of the global state that are mutable, one useful pattern:

  Do
   Latch a mutable reference to an immutable object
   Generate a new object based upon the latched reference
 Loop While CompareExchange fails.

The exchange exchange must update the changed link to the new one if it still points to the old one. This avoids the overhead of locking if there is no parallel access, but can work worse than locking if many threads try to update the same object, and generating a new instance from a locked one is slow. One advantage of this approach is that there is no danger of a dead end, although liveLock can occur in some situations.

0
source share

Another functional approach to this problem is to step back and separate the idea of ​​the state from the idea of ​​your little guy.

Your state will include the position of your little guy, as well as the position of your buddy and his shot, and then you have some functions that occupy some or all of the state and do things like creating the next state and drawing a screen.

The questions of time that you are talking about when the things you want to parallelize are dependent on each other, are real problems that will not magically go away, although solutions can be more or less convenient in different languages.

Several offers have already been made, and there are many concurrency solutions. Central clocks and agents will work, like software transactional memory, mutexes or CSP (stylish channels go) and, possibly, others. The best approach will depend on the specifics of the problem and to a certain extent on personal taste.

As for turning the head, try not to depend too much on whether the thing is changing or not. The point of immutability is not that things do not change, but that you can create pure functions to make your program easier to reason about.

For example, an OO program may have a drawing function that iterates over all the objects in the scene and asks them all to draw themselves, where a functional program can have a function that takes state and draws a frame.

The end result will be the same scene, but the way the logic and state are organized is very different.

For example, I think that it is much easier to work with it when you have all the data here, in one large input block and all the drawing logic enclosed in some functions. There are also some pretty clean architectural victories - serializing, testing and replacing the front ends is much easier with such a structure.

0
source share

Not everything in your program should be immutable. The player’s position is what you expect to change. His name may not be.

Irreplaceability is good, but you should perhaps reconsider your approach to use more parallel solutions than simple “immutable” everything. Consider this

The AI ​​theme gets a copy of your position. You move three units to the left. AI shoots you based on your previous position, and hits ... shouldn't happen!

In addition, most games run in game ticks - there are not many multithreads!

-one
source share

All Articles