For simple loops, I prefer something like map/filter/fold when appropriate. I try to avoid chaining them, because that often turns the code impossible to understand and makes me feel stupid. If the loop can't be modeled naturally as a higher-order function, an iterator-based loop is easier to read than an index-based loop, which is better than tail recursion.
With complex loops, there is no way around concrete loops with comments describing the invariants and what each part of the iteration does.
Many problems can be modeled naturally with recursion. Such problems are particularly common with graphs. However, you should never use explicit language-level recursion unless you can guarantee that recursion depth will be small. It's easy to get a stack overflow, so recursion should usually mean a loop with an explicit stack of states.
I always prefer to walk thru trees with e.g stack/queue because it's easier to reason about / debug it for me
nums.iter().map(|x| if x > 10 break(?))
This little thing is the major problem of functional chains (this is only a toy version of the issue). Because lambdas are inside the scope is hard to make them truly part of the current execution flow.So, sometimes I change a chain of iterators to simpler loops for things like this. IF iterators where part of current scope, it will look far more natural, IMHO...
x.try_for_each(|y| {...; ControlFlow::Break} )Internal iterators and/or lambdas are at odds with imperative code. Some guy also yesterday ponder about this stuff:
https://www.reddit.com/r/ProgrammingLanguages/comments/pno38...
I've grown to sometimes enjoy how a recursive loop looks in code. Sometimes you put the end case first, sometimes you put the end case last, depending on which makes most sense while writing (or perhaps while editing while reading later).
With languages with loops and no pattern matching, I'll always write a loop as a loop, if I used one with both, I dunno, there's probably a few loops I'd do as recursion and most would be loops.
OTOH, Erlang is certainly functional, but it's very pragmatic as well, there's not a mention of monads, and one tends not to do a whole lot of weird stuff with closures (although you can). Sure, there's a good number of functions that take functions as arguements (which you can do in C anyway), and there are anonymous functions, but you get better error reporting if you use functions with names, so at least I tend to name anything with more than a few lines. I also tend to have very short functions, but that is somewhat influenced by stack traces only having function names and not line numbers when I started 10 years ago.
I think it's really just a matter of prolonged exposure to make recursion seem intuitive.