7 ms·
Admitting that Functional Programming Can Be Awkward
- jrockway 17y agoI think his program would be harder to implement in C than in Haskell. It's all about what you are experienced with. (The usual pattern is "transform :: world state 1 -> world state 2". The transformer can "modify" as much of state 1 as it wants.)
- iamelgringo 17y agoI'm not trying to be snarky, I'm puzzled and interested in learning. How is passing around world state from one transformer function to another an improvement over traditional stateful programming where functions change variables?
- stonemetal 17y agotraceability and testability. update world1 is traceable you know just by looking at it what it modifies (sure in the example given it is opaque since it is the entire world) but if you consider that update world is probably broken into update world.player update world.ai etc. Then it becomes easier to trace execution because there is no implicit state. The lack of implicit state also improves testability. I can create a world and send it to update and know that I don't have to do any implicit state priming.
- didroe 17y agoCouldn't you do the same thing by just not using global variables in an imperative language? Then you get the benefits you're mentioning without lots of pointless data copying. You just pass a reference to the world object and you can see exactly what's being modified.
- silentOpen 17y agoIt is true that persistence is a default property of functional data structures but with a compiler or interpreter that isn't braindead you shouldn't get "pointless data copying" unless you hold and read both states after the transform. That is, it should be optimized away.
- didroe 17y agoI stand corrected. Don't you still have to do lots of messing around (in comparison to an imperitive language) to simulate state though?
- stonemetal 17y agoI am still new to functional programming, but so far it seems like I produce two kinds of code. Functions that gather state and functions that modify state. Say in the above example of update_world world1 all this would do is break apart the world data structure and forward the pieces to their respective update functions and then gather the parts in to a new world. The individual update functions either do the break and gather thing or they do the actual computation on a small entity or set of entities. This makes a large number of small easy to test functions that do the actual work and several "middle layer" functions that are easy to test with stubs. Though it would be interesting hear how people who have been doing FP for a while handle it.
- aufreak 17y agoThe pure functional world was pretty much impenetrable for me for that very reason ... until I encountered Monads in Haskell. Monads have changed the game completely by providing a very clean encapsulation of side effects in general. There are people who are trying to generalize it even further using what are called "arrows", but just groking Monads in Haskell will give you a new understanding, somewhat along the lines of what learning exterior calculus does to your view of traditional vector calculus.
- stonemetal 17y ago>>without lots of pointless data copying. Think tail recursion. It is easily optimized. It presents a nice uniform interface that forces the right thing. Just look at .net the generated main. It replaces general global scope with an application object global scope that rather encourages "cheating".
- richcollins 17y agoA large, complicated world state for a FP language is no simpler than a large complicated graphic of objects w/ inheritance hierarchies. You see, the inheritance hierarchies are the world state. In either system, you have to model the graph in your head to understand how it works. Complexity is the problem, not referential transparency.
- ubernostrum 17y agoHis followup addresses this, and brings up some examples which he can't work out how to solve with that pattern.
- psranga 17y agoBeginner question: does this involve a lot of memory copies which can result in performance problems. "world state" sounds like every data structure in the program.
- jrockway 17y agowhich can result in performance problems "Premature optimization is the root of all evil." Computers are fast. On my machine, I can allocate, initialize, and garbage collect almost 4G of memory a second. This sort of thing is really fast, and is unlikely to be a performance problem. When all of your data is immutable, however, you can reuse the data that you didn't change. The canonical example is a binary tree consisting of 2^n nodes. Changing one node only requires you to allocate n new nodes. As your tree gets bigger, the copying overhead goes to 0. In the case of Haskell, the compiler is smart enough to figure this all out for you. So all you need to do is write correct code, and let the compiler make it work on a machine. (It's also theoretically possible to reuse structures by mutating them instead of collecting-and-reallocating. You don't do that as part of your game, though, you let the compiler's optimizer do it. It will do it right, you will do it wrong.)
- teamonkey 17y ago"On my machine, I can allocate, initialize, and garbage collect almost 4G of memory a second." When everything has to happen in 1/60th of a second, then all that garbage collection is a serious problem. Memory allocation and deallocation is expensive in games, especially on the consoles. For simpler games, yes, not a problem.
- jrockway 17y agoThe "you are not Google" principle applies here. Unless you are writing the next hit game for the Wii, you can afford to write maintainable code. If you need every last bit of performance that your (underpowered) hardware can provide, then you need to take shortcuts and hire the 200-member team that that development style entails.
- silentbicycle 17y agoNotable quote: That's one option: to admit that functional programming is the wrong paradigm for some types of problems. Fair enough. I'd put money on that. But it also may be that almost no one has been thinking about problems like this, that functional programming attracts purists and enamored students. The language features that help with the problems small programs have are intrinsically easier to show off than those that help manage larger systems. Beyond a certain point, I don't care how easy something makes a program that will never grow beyond a page, though. (If that isn't easy, your language has serious problems!) I'm more interested in how these techniques / paradigms break down, not how well they work on examples chosen for them. You can't always choose your problems. (And yes, one of the main things I've learned from Unix is that it's usually better to break large systems into a team of smaller components, but that's neither here nor there. Also, informed use of multiple paradigms is likely the best long-term approach, but that's not a position that necessarily translates well into sound-bytes.) Of course, it's easier to hype languages that let you do short parlor tricks, particularly on web forums. Also, he has a followup posted here: http://prog21.dadgum.com/4.html http://prog21.dadgum.com/4.html
- miloshh 17y agolanguages that let you do short parlor tricks Quite the opposite - functional programming is becoming very appealing as a practical alternative for building large, multi-threaded, correct systems. True, it might be a bit more tedious to write a program that type-checks, but as many (including me) have been repeatedly amazed, it often works correctly right away, with minimal debugging. But it also may be that almost no one has been thinking about problems like this Mutable state is a fundamental issue that every functional programmer deals with every day. Indeed, it might be sometimes frustrating to pass around state that in C would be directly modifiable, but the benefits are huge. Pure code has been called a "scarce resource", and that's exactly right - once it works, it will always work, no matter how huge is the system you build on top of it.
- silentbicycle 17y agoOCaml is one of my favorite languages. I use it, despite its many warts, because it works. (I think there's a much smaller, cleaner language waiting to get out, though.) I wish more languages had decent module systems, for example. The ML family does this better than most, but I bet it's not a very glamorous area to experiment in, because it's hard to really show the benefits of a well-designed module system in small examples. That's what I'm complaining about. BUT, pure functional programming is an intrinsically poor fit for some problems, and this is seldom addressed by hype. I think functional programming hype needs a reality check now, the same way C++-style OO hype could have used more way back when. I think we would all be better off if, instead of heralding it as the next technique that solves all problems, we work on getting a clearer idea of its strengths and weaknesses.
- cookieliaokai 17y agoGood stuff ! Do U Wanna find sexy partners to have fun? Please check out__--==* Sugarbabymeet.C'om *==-- _ where you can meet the sexy beauties, wealthy singles and even hot celebs. What are you waiting for? Find your sexy partner NOW!
- ajuc 17y agoRight now I'm trying to learn clojure, and write 2d arcade game with it. I've encountered similar problems (almost everything should be passed almost everywhere), but I think this is actually good thing to choose what part of game can change what beforehand. My architecture (so far - it's in early planning stage): 2 threads (physic and logic thread going 10 Hz and graphic thread going as fast as it can drawing world) objects are struct-maps with fields position, animation frame, life etc (all fields are functions of time, and can change between physic thread turns, but not in one physic thread turn). Graphic thread just gets time and for each visible object draws it at current position (calculated from time by objects position function). So movement should be smooth and not dependent on frame rate. physic thread in response to player input calculates new functions for objects (only for X next turns). These new functions are encapsulated in event objects, that are added to events priority queue. On each turn (of physic thread) events for that thread are executed - each event takes set of game objects (not always all objects) and time and returns changed set of game ojects. In C++ this will be much easier to write at first (no hard thinking needed:)), but later it could become nightmare to change because of side effects.
- ido 17y agoIt sounds like you are overengineeing it. Modern computers are fast; Program it naively and if you see it's too slow (which it probably wouldn't be) optimize the bottlenecks. I am pretty sure you wouldn't need to make this multithereaded.
- joeyo 17y agoHis example is amusing to me because state machines are one of my least favorite things to code in imperative languages (especially C). It's so easy to forget an infrequently occurring state-interaction or side-effect. So if you want to be rigorous you end up with a meticulously detailed switch block (or whatever the idiom is in your language) with a lot of repeated code and global variables everywhere. My kingdom for a few closures!
- utx00 17y agocan you outline a sketch? closures by themselves do not guarantee the absence of side effects ie: (let ((state 'someobject)) (defun phase1 () (done1 state)) (defun phase2 () (done2 state) . . . and so on
- joeyo 17y agoSure; there's no silver bullet. My remark about closures was more to address his criticism that packaging up and passing around state was too much work to be worth it. Programming in Lua has a nice example of a maze game where state is passed around by emitting functions. It's a toy problem, but I think it's instructive: http://www.lua.org/pil/6.3.html http://www.lua.org/pil/6.3.html
- barryfandango 17y agoCue the apologists!
- utx00 17y agoi think pure functional programming is not a practical possibility. functional systems are functional until they are not (scheme's set!, clojure's transactions, ....) maybe the important thing is to keep in mind which parts of your system mutate data, and which parts do not.
- didroe 17y agoThis is exactly the idea I had a while ago. I was talking with a friend and we were designing a language where you would have pure and impure functions. That way you can easily see what's going on and what to expect but you don't have to jump through a load of hoops when modifying a bit of state would have done the job.
- mgreenbe 17y agoRight -- like Haskell, for example. Mixed paradigm programming is surely the way to go; ideology is surely the wrong way. C and OCaml both support mixed programming, but Haskell supports it better: monads allow you to isolate the side-effecting portions of your program. It's funny that the OP mentions Backus' FP, which had a "job control" language that functioned like the IO monad.
- utx00 17y agoor even adding an "n" in front of the function name goes a loooooooong (emphasis on the "o") way towards helping. nconc, nreverse ...
- radu_floricica 17y agoGood read, but shouldn't be controversial at all. Functional and imperatives both have their strengths, and that's why Lisp has always stayed in the middle btw. Also I don't think I heard any serious "purists" lately. Aren't we fighting windmills here?
- deleted 17y ago[deleted]
- stcredzero 17y agoAn arcade game should be organized using a game loop. Just make the game loop update the state of the entire game world each frame. This makes it relatively easy to write most of the game in a functional fashion. (You may have to have n "phases" in your game loop. Movement phase, collision resolution phase, etc.) The OP is accustomed to thinking only in terms of mutating state. He can't get away from it. He wouldn't have had all of these problems if he'd just done ye olde AI class with turn-based board game project in Lisp. (With a prof who set a coding standard for pure functional Lisp.)
- didroe 17y agoHow do you solve the problems he mentioned in his followup post? He seems to be saying that there are a lot of people going on about how easy it is but no examples beyond simple games.
- copenja 17y agoThis same guy also wrote an article describing the exact same functional style game loop that you are describing: http://prog21.dadgum.com/23.html http://prog21.dadgum.com/23.html
- stcredzero 17y agoI stand corrected. He knows his stuff already. I'll reread his OP.