> Can you? Like say you changed you sum() function to only sum every other element, because for the code you're working on right now that made sense. Well now you've screwed up the other places which relied on sum() summing all the elements.
I would not change sum but just filter out every other element and feed that new list to the sum function. (If you need it often, write a new helper.)
Your sum function should not decide which elements should or should not be added, that is the callers job. It doesn't even have the context to decide on that.
So yes, I would have the guarantee that nothing would break because I did not change sum in the first place.
In practice you probably wouldn't even start with that sum function but simply implement a function that takes two numbers and adds them together. Then the caller can use higher order function like fold and filter to do whatever it needs.
(Of course we assume it is not literally adding two numbers but some complicated logic, otherwise don't even write a helper for it, just sum your stuff when you need it.)
And yes, you can still have logic bugs in functional programming languages and it can't really protect yourself from that when refactoring but I wanted to note how keeping your design simple and having easy to understand composable functions can help avoid bugs.
> I would not change sum but just filter out every other element and feed that new list to the sum function.
Of course, and I would do the same in my imperative program. A dumb example like I said.
> Then the caller can use higher order function like fold and filter to do whatever it needs.
Right but then at some point you have a chain of ten of these calls, and you have it _all over_, and so you figure hmm lets make a separate function out of that, code duplication is bad after all.
And then you find your new function has a bug and you need to change it... Will all the users of this new function be OK with that bugfix?
If you don't wrap up these chains into new functions, how do find all the places you need to change once you need to make that bugfix?
> having easy to understand composable functions can help avoid bugs
Sure this I get, which is why my imperative code also contains lots of "do one thing" methods that is used as Lego bricks. And I use a fair bit of functional-ish code, ala LINQ, where it makes sense.
I wish I had discovered functional programming at an earlier stage, where I had more time to experiment. I think it would be very informative to make two non-trivial feature-equal programs in either style so I could compare.
One thing I want to point out is that when I first read "do one thing", I thought I knew what they were saying and I didn't. It took a long time for me to finally grasp that message.
One of the ways I finally learned that was after learning async/await in C#. Every beginner to async methods dread in fear once they realize the "zombification" of the Task<T> return type, where if you call an async function 6 layers deep into your program you need to change the return type all the way up the stack. Almost always now I call the async function at the very top layer. If I need some value or list I compute that list or value and return that all the way up then make the async call at the top based on it.
I learned to split my functions into two types, pure functions and impure functions. Async functions are an example of an impure function. The only thing those functions are allowed to do is their impure thing. Make a web call, push a value into a database, whatever. The pure functions are where you actually do your computations and transformations.
If you have a pure function that has a bug and you need to change it, because it's a pure function it is inherently testable. Just run it and see. If you're not sure, it's trivial to create a new function with the bugfix and only call that function from the places that you are sure of. But then try to make sure.
I would not change sum but just filter out every other element and feed that new list to the sum function. (If you need it often, write a new helper.)
Your sum function should not decide which elements should or should not be added, that is the callers job. It doesn't even have the context to decide on that.
So yes, I would have the guarantee that nothing would break because I did not change sum in the first place.
In practice you probably wouldn't even start with that sum function but simply implement a function that takes two numbers and adds them together. Then the caller can use higher order function like fold and filter to do whatever it needs.
(Of course we assume it is not literally adding two numbers but some complicated logic, otherwise don't even write a helper for it, just sum your stuff when you need it.)
And yes, you can still have logic bugs in functional programming languages and it can't really protect yourself from that when refactoring but I wanted to note how keeping your design simple and having easy to understand composable functions can help avoid bugs.