6 ms·
You need to cheat. Basically like react, the part that does mutation needs to be a framework segregated from most of the actual logic.
by crimsonalucard 7y ago
You need to cheat. Basically like react, the part that does mutation needs to be a framework segregated from most of the actual logic.
- galaxyLogic 7y agoCheating sounds bad. But if it makes it run faster, without any bad side-effects that would seem ok, and I'm not sure if it should even be called "cheating". Basically as I understand it functional languages must internally "cheat" all the time, because they must run on top of non-functional hardware abstractions. Tail-recursion-elimination happens by turning what looks like a recursive function into a while-loop internally, updating some mutable data.
- crimsonalucard 7y ago>Basically as I understand it functional languages must internally "cheat" all the time, because they must run on top of non-functional hardware abstractions. By cheating I mean functions must eventually execute a side effect and that side effect is usually mutating state. The IO monad is a cheat depending on your perspective. The very pixels on your screen are entities with states: RGB which even a "pure" program like haskell will be modifying. What haskell does is call these pixels IO and it segregates the actual mutation from the rest of the program. This is just my opinion but the main benefits of functional programming lie not in performance but in organization of code or what most programmers refer to as "design." If you're looking for extreme performance FP is not the the way to go. Assembly language is the way to go, but even before that I would pick a procedural language like C long before I use an FP language.
- whateveracct 7y agoChoosing a language for performance first when developing your game is silly. Many games (even AAA) have a lot of logic in some interpreted language. Haskell or Idris can be the equivalent of these (and they're way more performant) and you can easily writing custom asm or C or Rust or whatever via their (very good!) FFIs. But saying "oh computers are inherently mutable" is a pretty worthless and shallow statement. Nothing about that precludes FP, and FP works fine in those settings (Haskell can definitely approach C level performance if you know what you're doing) But you're right that nothing about FP is for performance. I don't think anyone would assume Haskell is inherently faster than C. It makes no sense. The closest you could say is some immutable data structures enable performance without much effort in certain use-cases.
- crimsonalucard 7y ago>Choosing a language for performance first when developing your game is silly. It is absolutely not. Components of the game can be done with an interpreted language but if performance is key, which it is for triple A games, then the core language is important. Scripting languages in games tend to deal with IO a lot. If your program contains too much IO it negates almost all the benefits of FP. Procedural programming is better for modeling temporal phenomena which is the core subject of what most of these "scripts" are attempting to model. >But saying "oh computers are inherently mutable" is a pretty worthless and shallow statement. I beg to differ. Saying otherwise is the worthless and shallow statement. >Nothing about that precludes FP, and FP works fine in those settings (Haskell can definitely approach C level performance if you know what you're doing) There are many things that hinder it but not preclude it. By common sense in general, it is harder for FP to get more performance out of the system. The logic is simple, the functor from functional programs to assembly language programs cannot be written in a way where it is bijective meaning that functional primitives have to map to another category where the objects are ordered tuples of multiple assembly language commands. In C you can very much write a program that is in spirit bijective with assembly. By that logic FP in general will be harder to control because every FP primitive means an almost unpredictable amount of assembly instructions. Performance improvements will tend to appear as leaky abstractions or hacks in FP. Especially with the call by need model in Haskell optimization is even harder.
- dwohnitmok 7y agoWhile it is perhaps useful to think of tail-call elimination as changing a recursive function into a while loop is a useful first approximation (and is how it is implemented in e.g. Scala), this is not really an accurate picture of tail-call elimination in general. Indeed it breaks down when considering mutually recursive functions. Tail-call elimination is simply the observation that in a tail position you do not keep a reference to the parent function around after you've called the child function. If function calls happen on the heap instead of the stack for example, then tail-call elimination is done simply as a matter of course by the garbage collector.
- dwohnitmok 7y agoEventually you need to cheat (or more accurately in the case of Haskell you need an impure runtime system that can interpret all of your pure code) to interface with the outside world, but not in the scenario the parent comment has laid out so far. Persistent data structures (structural sharing) is not cheating. Persistent data structures are immutable just as if updates to the data structure had been implemented by wholesale copying of a data structure rather than just updating the part that needs updating.
- crimsonalucard 7y agoEven with structural sharing you need to cheat. The program needs to feed that data structure into a black box that is mutating the actual hidden game-state. Somewhere in the entire game pipeline there are mutations happening at 60 FPS, and pure FP languages hide this from you by "cheating" while other languages make this fact explicit.
- dwohnitmok 7y agoOther imperative languages do not necessarily make this fact explicit. For example, elsewhere in this thread I present an example of structural sharing with a tree structure. That code could easily be adapted into, say, Java. There is no mutation going on at the language level. And that tree could be the entire game-state. The closest thing to cheating that's going on in that example is that GC is happening and hence memory will be recycled. Note however, that this is a fact not directly accessible to Java, whether you're writing imperative code or functional code. Imperative code is also "cheating" here, where it assumes that each new object instantiation is "new" memory, when in fact it may be old memory that's been reused. If you're writing FP code in Java, eventually you will need to cheat when it comes time to write out your data to the screen and when accepting user input (if you're writing it in Haskell eventually the runtime cheats on your behalf). But if your game-state is an immutable data structure, the update of game-state itself does not require mutability at the language level, even if you're writing in a traditionally imperative language.
- crimsonalucard 7y ago