6 ms·
Why do so many feel so strongly against state? In my 10+ year programming career not once have I ever wished something was functional. State simply is, dealing
by pdntspa 3y ago
Why do so many feel so strongly against state?
In my 10+ year programming career not once have I ever wished something was functional. State simply is, dealing with it is a foundational concept in writing code.
"It's harder to test" -- so mock it in the tests.
"It's harder to reason about" -- you can see the references in your code and you can punch it into the debugger. If the state is implicit it is mentioned in the documentation that you've read right??
"It's hard to tell where changes come from" -- again, stepping through with a debugger makes this somewhat trivial. Find the line that turns state bad, then drill into whatever that line calls, repeat.
I'm not saying we should be adding external state willy-nilly. But it is hardly evil -- the dumb hacks that have appeared when a class or instance var would have done the trick boggle the mind.
Can someone please prove to me how FP isn't just another cargo cult?
- pjerem 3y agoWhat do you mean by state ? Nobody is against "state", like, for sure, you need your data anyway. What I see that people are calling "state" is just deriving your final state from your original data, like in FP but saving your derived data into memory with a different lifetime than your og data. FP style don’t promote anything complicated other than just don’t save your derived data, just recalculate it each time. Doesn’t mean that you can’t have performance : in fact most of the time, the memory is the bottleneck anyway. Storing your state makes you responsible for synchronization which is where your bugs are going to hide.
- pdntspa 3y ago> What do you mean by state ? External state. Class vars, instance vars, even (gasp) globals. OP goes on some slight tangents/comments regarding FP and state management which inspired my comment.
- youraverageuser 3y agoProbably because it is very easy to mess up when using state incorrectly. It has been years since the last time my program run without bugs for the first time. If you're a genius, then sure, whatever tool you may be using, you'll do fine. But I noticed that many mainstream programming languages introduce concepts that are easy to misuse, even the same global variables. I recently wrote a small python script in 40-50, having concise and separate functions, etc. And I still was stuck on a bug that turned out be a consequence of shared state. The real issue was far from the location I was observing the bug. I think those kind of bugs is quite common. What it boils down to in the end, is your priorities. If you want a program that works 90% of the time and you don't care about the last 10%, then sure, stating your way through is quite natural. If you're sick of the small bugs that is the consequence of the language semantics and not the logic or design, then you'll start nailing down the factors that contribute to the erroneous nature, with state being just one part of it. Based on my experience in python and trying to teach other python developers some fp (spoiler, it didn't work)
- pdimitar 3y ago> Class vars, instance vars, even (gasp) globals. No need to focus on language-specific implementation details. A map that you are passing around in Elixir will serve you just fine 95% of the time. For the other 5% you can store state in an Agent, GenServer or in the ETS node-wide cache (here are your global variables). Not sure why you call FP a cargo cult btw, but in case you are interested in feedback: it makes you sound like arguing in bad faith.
- travisgriggs 3y agoI view it as pendulum. I cut my teeth during the sunrise of OO doing Smalltalk, dining in the halls of Smalltalk and CLOS. And then like a religious explosion, everyone went "all in" for random and arbitrary definitions of "all in" to OO and we ended up with a huge mess. Now, everyone hates on OO. It's sad to me. While OO has been on the outs for the last 10ish years, I feel like we've been awash in the other side of the coin. Instead of "all objects all the way down" we get all "all functions (and monads)" all the way down. There seems to be two "middle of the road" paths out of this. You either get "multi paradigm" approaches where you get incomplete and difficult to reconcile partial implementations of a smorgasboard of the author's preferences and formative experiences writ large. The other is the less popular but kind of "let's just be pragmatic" approaches like Elixir/Erlang. Personally, I'm finding the latter more appealing. Multi paradigm languages create a "too many knobs" problem when solving problems. The simpler, pragmatic, but sometimes kind of kludgy/goofy languages allow me to just solve problems (even if I have to jump through a couple of hoops some times), instead of wondering which hoops are the best hoops to jump through.
- el_oni 3y agoErlang and Elixir are what could be called pragmatic FP. The idea is that your base abstraction should be functional, you can then have a genserver for that abstraction which is stateful. So let's take lists for example. Building up a list is functional. But I could have a ListServer which takes an item and prepends it to a list. And just holds on to that. So lots of processes can send it messages to add things in, and order in the list is determined by order the messages arrived. The way the server manages state is State -> function -> State' And those functions are easy to test. It makes for a nice experience building software, the default is functional and you drop in processes which hold state when you need them. Compared to classes where the default is state.
- elbear 3y agoThe point is that it's desirable to reason about your state just from reading the code and not by using a debugger. And when state is changed and passed around explicitly, it's easier to do that.
- boxed 3y ago> In my 10+ year programming career not once have I ever wished something was functional. I.. have trouble with this statement. I bet you have, but you thought about it differently. A function that COULD be a pure function but that takes some data structure then mutates it and returns nothing, well that's just bad programming. Now, wanting the entire program to be functional, that's a different matter.
- pdntspa 3y ago> well that's just bad programming. There are a lot of situations where it is/may seem prudent to pass a reference to something, mutate it, then return
- hmmokidk 3y agoOnly reason I can think of is performance.
- pdimitar 3y agoYou can do that in Erlang/Elixir too, though you are passing a name/ID of the thing, not a reference to the thing itself (not in the sense of C/C++ or Rust references anyway).
- boxed 3y agoThere are. Yes. But the previous poster seems to imply they have NEVER seen code written like that that would be improved by making it a pure function that returns a value for the caller to assign. Never? That seems unlikely to me. Unless they've only worked with really really amazing programmers. Even then it seems unlikely.
- dns_snek 3y agoYou can "just" do any of those things but code that is understandable without a debugger is strictly better than code that requires a debugger to grok. > "It's harder to test" -- so mock it in the tests. This however doesn't make sense, you can't "mock" state - the state and its side effects is what you're testing.
- pdntspa 3y agoI guess it depends on the specific language but in my experience one can mock nearly anything the code references
- josevalim 3y agoYou may be misunderstanding the relationship between FP and state. We are not against state, quite the opposite, FP likes state so much that we put it on a pedestal! We make it explicit, often with functionality that makes it easy to spot, understand, and manipulate. If you think about your questions, here is how I would answer it: "It's harder to test" -- so let's make them simple to test without mocking them out (which would effectively remove them from the test). "It's harder to reason about" -- so let's make it easy to reason about by making it explicit. "It's hard to tell where changes come from" -- so let's add constructs for manipulating it. It is not about antagonizing it, it is about making sure it is clear and intentional. --- PS: the "evil bit" is more often associated to global mutable state, but don't let any FP language fool you, they all have global mutable state with explicit tools for manipulating them. In Elixir, you can even use tools such as LiveDashboard [1] to navigate all global mutable state in your application, making it very easy to introspect. [1]: https://github.com/phoenixframework/phoenix_live_dashboard/ https://github.com/phoenixframework/phoenix_live_dashboard/
- lawn 3y agoNobody is against state. But mutable state, that's another thing as it's harder to test, to reason about and leads to more bugs. It's not evil, it just has some drawbacks. Code that you have to use a debugger to reason about just isn't as good as code you can reason about without one.
- minthara 3y agoBrooo look there is no state! Except that there is this massive state object being passed around! I have experience from one Elm project at work and it is precisely like that, some massive state object that is passed around. No one wants to work with that project.
- jacquesm 3y agoState is unavoidable. But FP, rather than being just another cargo cult is simply a different approach to programming. In a nutshell: imperative languages arrive at a castle through piling stones on top of each other until they have the shape of the castle, and functional languages transform a pile of stones into a castle. The difference is subtle: the one is about how you do the work, the other is about the end result. There are several major consequences from using the one or the other technique and both have their advantages and disadvantages. FP is probably easier for people with a math background and imperative programming is probably easier for people that look at programming in the same way that you'd look at building something out of real world components. State, that big bad thing is something that is unavoidable in programming because at some level you will have state and side effects. But the longer you can put off having state (preferably all the way to the top) the cleaner your code will be and the easier it will be to test. That is because state effectively complicates the number of dimensions that your test has to verify against. If there are 5 variables of state and 5 function parameters that may still be doable. But if your execution context contains 500 variables and your function takes 5 parameters you now have to test against all of the permutations of 505 elements to ensure your function does what it should do. And if your function is going to modify that state (rather than return a result) it gets even worse for every function that calls this one. So functional programming helps in the same way that processes can encapsulate a certain amount of functionality within a limited scope: the scope of a function that has no side effects (so it does not modify the state of the program) and that does not look at anything other than its parameters is very well defined, if it works once with a certain set of parameters it will always do so with those parameters. So now you can write some pretty clean and simple test cases to verify this function once and for all and if it passes the tests you are able to make some guarantees about it. This can turn your worldview of programming upside down, it is a very powerful technique and to suggest it is 'cargo cult' is potentially throwing out a very powerful tool. hth
- sodapopcan 3y agoWhy would I want to mock when I could not mock? Why would I want to jump into a debugger when it could be explicit in code? Why would I want to jump over to documentation (which, if necessary, I did read btw, but thanks for the nag) that might be unnecessary. As others have pointed out, state is not bad, but implicit is (not to say we are never implicit about some things in Elixir). All of this stuff is achievable in OO languages, just not always idiomatic. Elixir is kind of an OO language. Processes are kind of like object instances with a bunch of added features that mainly make concurrency simple. It also forces you to be more mindful of when you want that kind of state (though processes don't have to have state). It's also hard to describe but immutability feels really good. Sure, you can say that it's no big deal to think about implicit references and and generally not get tripped up by them, and you'd largely be right, but not having to think about them at all is a nice little weight off the mind.