Hacker News new | ask | show | jobs
by BoiledCabbage 2327 days ago
Why is separating pure functional code from imperative / side effect causing code easier to reason about? You still have to have the side-effect somewhere.

Why are immutable data structures easier to reason about? They still produce the same results.

None of the above are proven facts, but they are generally agreed upon principles that a number of people have observed. And I happen to believe them as well.

Overall my opinion of why it's easier is that like most optimizations it reduces the number of states/cases you have to think about in the "common path". With a solid abstraction you rarely need to think about the internals, with explicit code you need to check and think through all of the edge cases.

filter() (or map, or ...) is probably the best simple case I can think about. There is more room for bugs in an explicit (ex. for (int = 0; i < ...) {}) than in a filter over a collection / enumerable. When reading the code you need to think through base and termination conditions as well as confirm aggregation is happening correctly. With a filter() you don't have to do it / be explicit about it. Obviously you still need to know what filter means (which means you know it's implementation) but you don't need to focus on the details, only the filter condition: the signal amid the noise. The rest is pushed behind a solid reusable abstraction. Again thinking about it in isolation if someone just introduced the filter function to you you might look at it an say it really doesn't add much and if anything obscures my code I don't see the value. But after adopting it, and it's related family of monadic collection operations, code can now be written at a much denser, fewer off-by-one errors, higher signal to noise, level of abstraction. It's provided structure and common abstractions to unstructured iteration. To the point you begin to think in these abstractions (which I think is a benefit).

Another example are parser combinators. There is nothing that parser combinators do that can't be done by hand. But one of the primary way they simplify coding is by hiding away conditional (and looping logic). "?*.." , | and & concepts. Parser combinators also benefit in simplifying things by building an algebra for users to work in with nice closed operations. But again that only became possible by abstracting away the conditional and looping logic and making these nice simple abstractions that can be easily combined.

So my summary is, my view is that abstracting over control flow code does a lot to simplify logic. A lot of logic involving looping has already been abstracted over and included in languages and libraries (in general "functional programming" styles). Now that the low hanging fruit has been incorporated I think next will be the next layer of non-looping conditional logic.