3 ms·
> You could pass a global state in and out of every function in your program, but why not make that implicit? Because making it explicit allows you to reason a
by sparkie 12y ago
> You could pass a global state in and out of every function in your program, but why not make that implicit?
Because making it explicit allows you to reason about what a piece of code does without inspecting every little part of it for the global state it touches.
- ajuc 12y agoAt the cost of having to practicaly rewrite it if the control flow changes even slightly. If you don't know the exact control flow when you start it's a lof of busywork to modify it each time you change your mind.
- sparkie 12y agoI'm not sure where you get the idea that much would need rewriting, but if anything does, the advantages of having modular code that you can reason about are worth it. If you read what I quoted again, he is basically arguing for the use of plain old imperative code over OOP. It's widely considered bad practice to use global state at all in OOP languages, except where strictly necesssary for handling specific resources anyway. OOP languages still have global variables via "static", but good OOP code design will try to avoid static entirely, and pass any global state explicitly down through functions and classes all the way from main. The reason is simple: It allows you to reason about what every part of code does. The only problem is that you can't reason about some code someone else wrote, as it may use static internally. And this is really what FP intends to fix. Instead of just "trying" to avoid static, forbid it entirely. Now you can take some code written by anyone and be sure that it isn't going to interfere with another piece of code you write separately. Any time you explicitly want parts of code to interact in such ways, making those interactions explicit still allows you to reason about their collective behavior (and isolate that behaviour from the rest of your code). Hague suggests that with FP, you need to write in SSA form whenever you're passing state around explicitly, but this argument is nonsense - FP has all the tools to avoid the use of assignment at all. Control flow is abstracted away as functions and they have useful names (or operators). What makes Hague's suggestion most absurd though is his use of an example from imperative languages, where he uses an if STATEMENT. Of course, you probably do need to use variable assignment if your language prevents you treating constructs as expressions.
- ajuc 12y agoSometimes it's worth it, sometimes it's not. Exploratory programming is a thing. Example: Spec 1.0 update position of objects according to velocity for in imperative, map in FP Spec 1.1 what if we made the objects that are far from the center of the group move towards the center? for followed by for in IP, reduce and map in FP Spec 1.2 scratch that, just remove the objects that are far from the center for and for in IP, reduce, map, filter in FP Spec 1.3 and spawn explosions around them add 1 call in ip, refactoring required in fp Spec 1.4 also they should play explosion.mp3 add another call in IP, another refactoring required in FP Spec 1.5 this should happen for all destroyed objects move both calls to function that removes object in IP, another refactoring (with passing a lot of state in new ways) in FP And so on. After you're OK with the result you can make the code nice in IP too. And you can do this once, instead of doing this every time anything changes. It's like a text editor that forbids you to have temporarily non compiling code. Only refactorings are available, you can't write if by hand. Pain in the ass. Yes I know about paredit, people still copy paste, and write parents by hand.
- sparkie 12y agoIt'd be nice if 1.3 and 1.4 could be done with just 1 line of call, but you're missing the fact that pretty much any graphics and audio library will have some "context" singleton through which it is called due to the way these interact with the operating system. The APIs are deliberately designed this way to encapsulate the global state they use. Sure you can make a global variable for the context and avoid passing it around - but the refactoring route is the preferred option in the imperative world anyway. If you're writing in Haskell with any expectation of using IO for some exploratory programming like this, the obvious solution is to make your functions of type (:: MonadIO m => ... -> m ...), then any refactoring you need to do to add additional facets to your state can be specified elsewhere as part of a MonadIO transformer. Your calls to "play explosion.mp3" and "render explosion" just need a liftIO on them.