5 ms·
Imagine you've implemented a large program in a purely functional way. All the data is properly threaded in and out of functions, and there are no truly des
by blakehaswell 11y ago
Imagine you've implemented a large program in a purely functional way. All the
data is properly threaded in and out of functions, and there are no truly
destructive updates to speak of. Now pick the two lowest-level and most
isolated functions in the entire codebase. They're used all over the place,
but are never called from the same modules. Now make these dependent on each
other: function A behaves differently depending on the number of times
function B has been called and vice-versa.
That sounds like a terrible design decision.
Having two functions which are used all over the place by different modules, and then linking their behaviour to some piece of state that wasn’t passed into either of them is a terrible idea. Suddenly that state becomes an implicit argument AND return value, and I have to know about that before I can call the function… Not only that, now I’ve lost my ability to reason about the behaviour of the function independently from the execution of the program. Further, functions A and B are now impossible to test in isolation. I need to actually call A multiple times before I can test B properly. Then once I’ve done that, well I’ve already called B a few times, so how do I test A? I’d have to have assertions for the behaviour of A and B tangled up in the same tests…
This is a mess, whatever programming language/paradigm you prefer.
- melted 11y agoThat's also sort of missing the entire goddamn point of functional programming. The point of functional programming is that functions produce the exact same thing every time. That's why you don't have to evaluate them until the end value in the chain is needed, and why all state is nicely encapsulated elsewhere (e.g. in IO monad). If that state changes, the entire evaluation that leads to the final result needs to be re-done. It may represent a part of the overall evaluation. There are no ifs and buts here, even if you need a random number generator, you deal with it the exact same way.
- mjackson 11y ago> That sounds like a terrible design decision. The author never said it was a good design decision, or even a desirable one. He only said it would be impossibly difficult to do in a purely functional program, which is worth considering. Whether or not it's a good design decision is irrelevant.
- CHY872 11y agoHe's making the argument that functional programming is bad. If it were a terrible design decision then the language precluding its implementation (as he claims functional languages do) would be a positive or at least not a negative. So, he must be making the argument that it is a good thing, or at least a necessary thing. Even so, the ML's all support this type of behaviour (with globals). In the same way, we do not criticise functional languages for not allowing goto style control flow.
- golergka 11y agoIt is very relevant, because author tries to paint this difficulty as a bad thing. It isn't. If the language, or a tool, makes it hard to do bad things and makes it easy to do good things, it's good. http://blog.codinghorror.com/falling-into-the-pit-of-success/ http://blog.codinghorror.com/falling-into-the-pit-of-success...
- millstone 11y agoNo, the example is unmotivated, but not unreasonable. For example, say the two functions are computationally expensive, and you want to make them faster. The "piece of state" is a shared cache, i.e. memoization. Furthermore, there's reason to believe that if function A is called with value X, then function B will not be called with Y anytime soon. So if A(X) is called, Y(B) ought to be evicted from the cache. This is awkward to implement in pure functional languages, but it's not a terrible idea. You can still reason about the functions, test them in isolation, etc.
- thu 11y agoYour example is very different. It seems you're still describing a pure API: testing those new function implementations can be done with the same test suite used for the slower variants. In the blog post the author choose to specify that the observable behaviour is not pure.
- Veedrac 11y agoWhat if the cache involves IO? The behaviour is then assuredly not pure.
- ajuc 11y ago> This is a mess, whatever programming language/paradigm you prefer. You will of course refactor the code. The point is - it's harder to make a quick and dirty prototype to see if it's better than the previous version (very common in game programming - you don't have strict requirements, you iterate quickly and check "is this fun yet"). And elegant functional code changes more than elegant imperative code for the same change in requirements, so the refactoring would be bigger for functional code (on the other hand the functional code is probably shorter).
- dozzie 11y agoCongratulations, you have just made pseudorandom generators a bad decision.
- pjc50 11y agoWell, they're easy, convenient, and have subtle downsides: difficult to test code that depends on them without machinery to reset the seed, potential problems in a multithreaded environment, may be used in ways that do not produce evenly distributed outcomes, and for CSPRNGs a whole other bunch of crypto downsides. Banning RNGs from e.g. avionics control systems sounds more like a good decision than a bad one.
- dozzie 11y agoWhy yes, yes, of course. I fail to see where avionics need PRNGs (but I don't program avionics, so I might miss some uses). But surely UUIDs are as bad as random numbers in software for a plane?
- rbehrends 11y agoFor context: This is a simplified example derived from one that the author apparently got from an older discussion started by Peter Van Roy [1] that both of them were involved in (and which I happened to read back in the day). The argument here is about modularity, namely encapsulating internal state within a module without exposing that state to the outside. (For example, so that one can introduce a module-internal auditing mechanism or a cache without altering the module's API.) Peter Van Roy, of course, is one of the principal authors of Oz, so he may have some natural bias, but I've always found the argument persuasive. For testing and validation purposes, one will obviously need to use appropriate techniques (e.g. specifying and validating state invariants). [1] http://lambda-the-ultimate.org/classic/message9361.html http://lambda-the-ultimate.org/classic/message9361.html