Quite practical I imagine, there is no reason to keep all the frames in memory. Keep the last n frames and get most of the benefits. The point of this programming style in a game is to have a few previous states when there is a crash/bug to figure out how the state ended up in a bad way. Maybe save a framesworth of state every keypress for debugging purposes. Then on a crash or bad state there is a debugging environment all ready to go, for the cost of a small amount of memory. Good deal.
Although in practice "purely functional" programming is a bit silly here. The point of PacMan is all the IO which is fundamentally all side effects. There are a lot of practical compromises that would be called "functional" like storing all the major state of the game in a big array (something like [turn1, turn2, turn3, ...]) and then implement a turnX -> turnX+1 animation function.
The fundamental insight of the functional approach is implementing a frame->frame function was already something that implicitly had to be done to make the game. If it is an explicit function then debugging it is easier. Because the input can be saved. No extra work is needed.
> The point of PacMan is all the IO which is fundamentally all side effects.
In fact, the point of a huge proportion of programming is IO and is "all side effects." It's still possible to adopt a functional style for internal calculations (and in fact this helps to decouple components and makes testing much easier, at the expense of relatively negligible copying overhead, so it's worth doing) but once you get beyond your internal toolkit and start actually trying to test the whole system, you're interacting with the outside world.
Functional programming is not some sort of mythical effect-free beast… All functional languages I know (including Haskell and Clojure) do side effects just fine, and there are plenty of different approaches for performing effectful computation sensibly within FP.
Calling I/o side effects makes FP seem hopelessly academic. Everything having to do with the world is seemingly a corner case - the core of the programming philosophy is only concerned with the part where we push bits around uselessly.
To be sure, I don't think this is a correct interpretation, but I do think it may play a role in the difficulty of getting people onboard with FP.
> the core of the programming philosophy is only concerned with the part where we push bits around uselessly.
It's the opposite, for FP, OOP, imperative, that is the part of the program which is easiest.
FP is about managing the difficult parts, and so is OOP. They just have different approaches, and note, many mainstream languages are incorporating FP concepts quite readily now.
Mainstream languages incorporating FP concepts don't do so with the intent of encouraging totally pure stateless programs. What they do allow is calling the occasional anonymous function or using Map() instead of ForEach() or for().
Totally pure stateless programs are not the norm in functional programming. E.g. the functional core imperative shell pattern.
It's also not just a syntax difference, mainstream languages are embracing immutability more and more, often as a default.
The point is that calling FP academic is a tired trope and even if it is far from the dominant paradigm, there is clearly practical lessons to be learned whatever paradigm you program in.
The only time I hear about a mainstream language being immutable by default is from Rust people telling me that Rust is a mainstream language now.
A Haskell program covered in piles of monads to abstract all state away still follows the functional core imperative shell pattern, because the runtime is the imperative shell. This doesn't make such a thing desirable for other languages.
The practical lesson I learned from dabbling with functional programming in academia is that functional programming is often impractical and does not offer any of the purported benefits of better code.
> The only time I hear about a mainstream language being immutable by default is from Rust people telling me that Rust is a mainstream language now.
Most mainstream languages are old, they can't be immutable my default without breaking backwards compatibility. I'm talking about the design direction of languages that are already based on mutability. For example, Java added Records which are immutable, as well as overhauling the date/time api with an immutable version.
> This doesn't make such a thing desirable for other languages.
I've used this pattern in other languages, none of this is a silver bullet that makes software engineering easy; but I do find it makes some code much easier to test and reuse.
Monads are just one design pattern for handling state, and not the only way. It's no different than having an OOP using dependency injection everywhere - it's a design decision that you use only when it makes architectural sense.
A shell is not more imperative than a processor - both are Turing machines, a completely functional mathematical object. Seeing them as imperative is a semantic interpretation of the people watching it, not an inherent property.
Functional languages have either converged on either just allowing mutable values or relying on Monads as the ways of handling state. I'm not aware of a third solution.
FP is not stateless, is state-aware. You need to declare what functions handle state, just like in Java you need to declare which functions handle exceptions.
C# including LINQ or Javascript including Promises isn't done with the intent of having all functions handle state and pass around a state of the world parameter (trying to actually do this in Javascript will lead to a lot of boilerplate and ugly corner cases, as one would expect in the language).
Well, that's partly because these languages were never designed to be functional in nature, and partly because the approach you propose is not how FP works. I'm not sure what else you'd expect?
If someone tells me that languages are adopting a functional approach, I would expect to see the tools and methods evolve to allow functional code to be written. What I am seeing is languages adopting specific functional methods that can be used or discarded, rather than encouraging the adoption of the actual style.
Regardless of language, you can structure your program as a pure, functional core and an imperative shell. All of the io happens at the boundaries, i.e. the shell, but the application itself consists of functional, side-effect-free code.
Some languages make this easier to model than others, and some make doing so in a pure style easier, eliding real-world side effects like RAM access, GPU, etc.
Once you are comfortable with this concept, functional languages make a lot more sense, if you don't get lost in symbol soup.
Edit: a simple Google search for "functional core imperative shell" brings up many writings on the concept.
Input is an obvious example, as it's impossible to predict the value of an input.
Do you mean this more abstractly or literally? In the example of PacMan, for every frame, the only possible input is nothing, left, right, up, or down. So you are be able to predict it is going to be one of those five, though not which one.
That is unpredictable, but not a side effect. Side effects are changes that are not declared as part of the function parameters.
If you model the controller input as a function parameter, that's just a state argument, and FP can handle it declaratively just fine without having side effects.
I’m asking as someone not familiar with function programming, so trying to learn, not be pedantic, btw!
So because the possible input is not just a single value, it is not predictable. It doesn’t matter if the input was limited to a set of two values, or infinite, it is literally not predictable so therefore we don’t know the future state of the program?
It's a bit odd that people often call these side-effects. They're just "effects". Side effects are things you don't want to happen, i.e. accidental/incidental effects... which is exactly what a pure language like Haskell aims to prevent.
That's not what "side effect" means in this context.
Side effects are the opposite of pure functions. A function with side effects will mutate global state, a function without side effects will not (it will simply return a value).
This is true and correct, but I would add that sometimes in FP books and papers you do see authors merely writing "effects" as well. I think either would be acceptable.
"Effects" in an FP context generally refer to effects explicitly modeled as values in the program, whereas "side effects" are implicit. `Either` is an effect, throwing an exception is a side effect.
Only if you exercise a high level of discipline in keeping all your state contained in the same place, having zero implicit state, and writing clonable data representations (i.e. no impredicative references, or some kind of serialization strategy that can deal with them). 99% of programmers do not write code this way when using imperative languages.
The Java Enterprise style of over-abstraction that everyone hates does this, so plenty of imperative programmers will know of it and probably have implemented it to an extent.
Although in practice "purely functional" programming is a bit silly here. The point of PacMan is all the IO which is fundamentally all side effects. There are a lot of practical compromises that would be called "functional" like storing all the major state of the game in a big array (something like [turn1, turn2, turn3, ...]) and then implement a turnX -> turnX+1 animation function.
The fundamental insight of the functional approach is implementing a frame->frame function was already something that implicitly had to be done to make the game. If it is an explicit function then debugging it is easier. Because the input can be saved. No extra work is needed.