Comment by busterarm
2 days ago
I never quite understood this recursion is hard/magic sentiment. Maybe it's because I started my CS education when it was still taught out of math departments or because it started with actually using programming languages to do algebra. Then again, the FP Kool-Aid is practically the blood in my veins at this point I guess.
I’m good at spatial thinking, so on paper I should have zero issues with recursive code. But I also mentor and troubleshoot a lot and deep recursive code blows everyone’s mind. Even, it turns out, often the people who wrote it. Though they will swear otherwise.
Self recursive code takes on a fractal nature - at any call stack you cannot determine the call depth. You are in a twisty little maze.
When you layer calls horizontally, different tasks at different depths, it’s dead obvious where you are in the calculation. If all you can manage though is iteration, you still have the local variables to tell you where you are.
I spend half my career with Haskell, OCaml, Erlang and we never had these problems with recursive code. (Or rather, we never had these problems and blamed it on recursion. We had to deal with plenty of bad code, of course.)
In contrast, I remember plenty of problems stemming from mutation (and lack of static typing etc).
Survivor’s bias. You’re in a weird sect of self selected programmers.
I certainly understand your perspective and I've seen what you talk about it but I've just never run into problems with it personally...
and yeah, as others said, mutation is often the source of more problems.
Generally though I don't like hard "don't use X" rules. There's a time and place for everything -- it all comes down to what you're prioritizing at a given time. I try not to be dogmatic about anything.
One of the big feats I accomplished on my last job was converting a Stygian nightmare of co-recursive code my “clever” coworker wrote into a tree scan and a single loop.
I fixed two bugs I knew about, four bugs nobody knew about, cut 20 seconds off of startup time per instance and 10 minutes off of deployment. And enough memory to run another process per box.
This is not even the first time on that job and it won’t be the last. In fact it was a theme. I landed half of the real improvements to response time and cluster size in about a quarter of the time investment, mostly by unraveling recursive bullshit inserted by two architectural astronauts. About ten percent was done by people following up on stuff I started. The rest was long slogs trying to do major architecture shifts while leaving the shape of the existing code alone.
To be clear: the iteration is not a super optimization weapon. It just clears the water enough that people can see what’s going on. This is to me the heart of “make the change easy”. You start refactoring for one problem and you find two more while you’re in there that were lost in the noise.
I’d say roughly half of the rest of my time was spent reworking code we ostensibly wrote for other teams that they balked at owning, in order to add features and convince them to take ownership of it. And almost half of that work was ironing out similar Rube Goldberg machines. How do you expect people to own code they can’t figure out how to maintain?
You won’t know if you haven’t tried, and “I haven’t seen this” is not a strong signal that you have. Keep an eye on the next coworker who talks like me and see what they’re up to.
I have seen this quite often. I blame it on the obsession with UML along with the limitations of UML. I/E in UML every one draws boxes that have arrows to other boxes, but no one draws boxes that have boxes inside them. Instead, you draw a box with an arrow going out and then back to itself, hence _it has to be a loop because the arrow does a loop_.
That's why React components are _weird and wrong_, SQL CTEs are _overly complicated_, and functional programming is _impossible to understand_. It's all boxes with other boxes inside them, instead of next to them, and many people can't understand you can nest boxes many times.