I think questions like this are really getting at, is how do you update the 'huge-game-state' without mutating it, changing variables.
People have a very ingrained feeling that the game state is updated by updating variables.
But in functional languages, the state is immutable, so if you are new to functional languages you ask 'man how do I update this'.
Then, if this new person takes next step and is told 'you make a copy of the game state', they are like 'man that must be a big performance hit to copy the whole thing'.
So I think questions like this are really about how to handle a state with immutable data. How to make a copy efficiently, like in parts, or whatever. And then of course, you are down the rabbit whole of changing your brain thinking towards functional style.
I would really enjoy a class that was nothing but modeling various kinds of computation entirely with recursion. Every time I accomplish something neat and clean with recursion, it feels like I just used a niche superpower.
> I would really enjoy a class that was nothing but modeling various kinds of computation entirely with recursion.
I had the luck of having a whole semester doing just that with various functional and logic languages (Haskell and Prolog) with a great teacher.
You start by learning accumulator parameters and tail recursion, then you build and learn to use foldl and lazy functions; and then you may learn monads as a design pattern to reduce boilerplate for those techniques.
It'd be good to hear some discussion of whether it's advisable and what the advantages and disadvantages are. Often discussions around functional programming are incredibly dogmatic, with it already taken as gospel that functional is superior in every way and purity is next to godliness.
Fair enough, my dogma is "understand the problem, then apply the appropriate programming paradigm, methodology and approach in the context of the overall system that you're building." And yeah, I believe pretty hard in this. So when someone tells me "you MUST use [procedural / object oriented / functional / graphical / vegetable / ironic] structures in your code, or it's BAD CODE and you are bad too" I tend to immediately think they're full of crap. This response is raised to the n'th power when they start using terms like "purity" to rate the philosophical 'goodness' of code in any way unrelated to its correctness, performance, simplicity, or ease of writing, verification, or future modification.
'Purity' is advisable for the same reason that 'not using goto', 'hygienic macros' or 'structured programming' are highly recommended (to the point that nowadays they are taught as THE way to program, and nobody would think to ignore them without a good reason).
Sure you can create good programs without them, and the first developers often did; but nowadays a good programmer is expected to understand why those techniques are relevant and what kind of problems appear if you decide to ignore them on purpose.
For complex modern programs, specially on the web, the 'pure functional core with iterative shell' (possibly imperative, but it can also be funcional reactive) is an increasingly common architecture that the most popular frameworks are converging to.
Not mentioning any of the complexity in the abstractions needed to ensure purity makes one a dishonest salesman, much like the OO consultants of the past did with Java and the Java frameworks.
The ills of goto and poor scoping were solved with compiler rules and very simple expressions that could be added to any language. The alleged ills of mutation as described by academic groups made up of mathematicians whose jobs very infrequently have to do with creating actually useful software often do not match the reality of coding, nor have they ever been so bad as to demand alternative solutions. The vast majority of languages had no problem eliminating GOTO; that only a small fraction have implemented purity puts it on the same level as hardcore OO, which also is only a small fraction of the market, and about as useful.
Claiming that functional programming is inherently more complex than imperative is also dishonest. Industry developers that criticise it without understanding the value and strengths of referential transparency are no better than your hypothetical academic groups.
All abstractions introduce complexity in terms of indirection and the need to particularise to different instances. Imperative and functional simply happen to have a different approach to where they place complexity and what parts they simplify.
Imperative makes it easy to update state, but then it forces you to keep in your mind every remote part of the program where your symbols may be modified (which is way harder than most developers recognize). Functional requires more work to keep track of state, but on the other hand it allows much better control of program composition and deep complex hierarchical data transformations.
As they say, use the best tool for each job. It makes no sense to deride a key wrench for being more complex than a hammer and being worse at hitting nails.
Imperative programming with state is as simple as functional programming without state. Functional programming that introduces or models state requires more abstraction, and then complexity.
>Imperative makes it easy to update state, but then it forces you to keep in your mind every remote part of the program where your symbols may be modified (which is way harder than most developers recognize)
Scoping inherently reduces the extent to which state is considered. Any imperative programmer worth their salt knows to minimize reliance on global variables - functional programming salesmen claiming a significant advantage by eliminating bugs involving them can't help but come off as more than a little condescending by belaboring this point.
>As they say, use the best tool for each job.
This is a big step down from "All programmers should know and implement purity by default because mutability is the next goto."
> This is a big step down from "All programmers should know and implement purity by default because mutability is the next goto."
Not something I said. I explicitly mentioned the functional core, iterative shell architecture, and explained how you need to understand functional enough to know when it's a good time to not use it.
At this point your posts look like someone making fun of a complex technology they do not understand, without really approaching any valid criticism of its real shortcomings, because such criticism require a thorough understanding of the thing being criticised.
> Functional programming that introduces or models state requires more abstraction, and then complexity.
I'm pretty sure I've said exactly that. Good thing we agree on it. But you don't seem to realize that this fact is only true for handling state, and not for other aspects of programming.
> Imperative programming with state is as simple as functional programming without state.
You've never tried to compose multiple asynchronous event streams of complex data types from different subsystems into one single user-facing unified presentation, have you? Your assertion is simply not true. Such feat is way simpler in functional reactive style, and a true nightmare in an imperative multithreaded program. That's why web frameworks are evolving to handle more and more reactive functional patterns for compositional asynchronous tasks.
Your comparison of mutable programming to the use of goto and unhygenic macros would suggest that mutation is a similar type of dinosaur to be culled.
>You've never tried to compose multiple asynchronous event streams of complex data types from different subsystems into one single user-facing unified presentation, have you? Your assertion is simply not true. Such feat is way simpler in functional reactive style, and a true nightmare in an imperative multithreaded program. That's why web frameworks are evolving to handle more and more reactive functional patterns for compositional asynchronous tasks.
Imperative programming with state is simple. Programming asychronous event streams of complex data types from different subsystems into a single unified presentation is complex. Functional programming claims to have abstractions that make the latter more simple than imperative programming, which may or may not be true.
The use of functional reactive style in React (I do not believe Angular relies on this) is probably driven by JavaScript having atrocious scoping rules and the general Web / DOM stack being full of hacks and badly written APIs. The hacks that are still present in React make it seem like this will just be yet another layer of cruft that is the front end, alas.
> Your comparison of mutable programming to the use of goto and unhygenic macros would suggest that mutation is a similar type of dinosaur to be culled.
Then you missed the point, which is that using mutation a.k.a. cells a.k.a. imperative code when it's not needed is a dinosaur to be culled. But you need to understand when it's not needed to realize this.
Functional reactive programming adoption is not something caused by the state of one particular stack; as it is also being adopted by game engines, big data analysis tools, development environments like web notebooks, etc. People throughout the industry are seeing the value and are simplifying their tools thanks to the paradigm.
> Functional programming claims to have abstractions that make the latter more simple than imperative programming, which may or may not be true.
Look, I get it, I really do. You don't see the possibilities of the functional paradigm and don't know how and where to use it effectively, so you keep criticizing the small part of it you do understand. I've been there, done that (though it was a long time ago).
With that attitude you can stay comfortably in a corner and be impervious to the changes the industry is undergoing, and miss out on a really cool programming style out of prejudice. Or you can accept the intellectual challenge (which it certainly is, especially for someone who has spent his entire career with a single style), and know concepts that those who learn them agree that they improve their programming even if they do not adopt them 100% for their work.
It's up to you. I don't gain anything with this, so I will not try to convince you of it.
>> Functional programming that introduces or models state requires more abstraction, and then complexity.
> I'm pretty sure I've said exactly that. Good thing we agree on it.
I strongly disagree with the complexity part of this statement. Yes, FP is in many ways "more abstract", but so is natural human thought. More abstraction is not the same as more complexity (except when the abstractions are inappropriate in a given situation), does not lead to more complexity, and the FP approach to state is objectively simpler in most* real-life scenarios.
I don't believe in zero cost abstractions. All abstractions have a cost of being understood and implemented. x = x + 1 is simpler than world = worldupdate(world(increment(x)).
Abstractions exist in our brains. They are costly when they are unfamiliar (requiring learning) or when they are inappropriate (the product of incompetence and causing pain for everyone). Sometimes they are exactly as costly as they should be, reflecting the inherent and essential complexity of the problem, which is ideal. Inappropriate can mean either “too little” or “not enough”… but more accurately, it just means “the wrong abstraction”. Making everything a class, or treating everything like a Turing tape, is also creating mountains of almost-certainly inappropriate abstractions, in most (but not all) cases.
This is very handwavy, because i’m speaking in very general terms. But for a single, narrow, non-general, concrete example, just look at the ORM impedence mismatch problem with OOP languages. AFAIK, after 40-some years it’s still unsolved. I don’t believe FP suffers from this specific problem (again, just to give a concrete and non-handwavy example), because FP is very analogous to relational programming (functions are a special case of relations).
This shouldn’t be so controversial and should be very intuitive to anyone who spends a bit of time thinking about it. A function is fundamentally a simpler building block than a class. It’s more atomic.
I don't believe an ORM is a zero cost abstraction either. Or most of OOP.
The cost of implementing everything as a function is when one has to contend with the fundamental machine architecture not dealing with everything as a function. This often leads to one of the highest cost abstractions out there, the Monad.
y = x + 1 is not dealing with state. Supposing there was a desire to have x = x + 1, the simple way of doing this is x = x + 1, and the complicated way is to do world = Increment(x, world).
Changing a variable is simple. Creating multiple layers of indirection to change a variable in a pure fashion is not as simple. That should be obvious and apparent.
It only appears simple because the layers of complexity to achieve that effect are hidden under the language runtime, and you're exposed to just the top of the iceberg.
If you had to build the whole runtime yourself from first principles, functional behaviour would be way simpler to define.
A functional runtime from top to bottom is simple on a FPGA. It's also pretty useless. A circuit with no memory that does the same thing each time. Pretty good for a light switch, but not the kind of things people want computers for.
I think questions like this are really getting at, is how do you update the 'huge-game-state' without mutating it, changing variables.
People have a very ingrained feeling that the game state is updated by updating variables.
But in functional languages, the state is immutable, so if you are new to functional languages you ask 'man how do I update this'.
Then, if this new person takes next step and is told 'you make a copy of the game state', they are like 'man that must be a big performance hit to copy the whole thing'.
So I think questions like this are really about how to handle a state with immutable data. How to make a copy efficiently, like in parts, or whatever. And then of course, you are down the rabbit whole of changing your brain thinking towards functional style.