On the contrary, I think recursion is a great tool and use it often in Julia; of course, it has its limits, but no more so than in most other performance-oriented imperative languages (e.g. C).
For example, it is a great way to exploit memory locality in data structures, as I demo’ed for matrix multiplication for example (or for FFTs). It can also be a nice way to express multidimensional algorithms, e.g. for multidimensional Chebyshev interpolation or for multidimensional in-place array reversal. Julia’s built-in sum
function uses recursion to implement pairwise summation for greater floating-point accuracy. And, of course, recursive sorting algorithms like Quicksort and Merge sort are famous, and Julia’s sort
function has both recursive quicksort and recursive mergesort.
But you have to use recursion appropriately. If you are recursing thousands of times in an imperative language, you should probably consider a loop (perhaps with an explicit stack/queue data structure). If each recursive call is extremely cheap, then you should consider “coarsening” your recursion (e.g. by enlarging the base case) to amortize the function-call overhead (this is true in any language, BTW, at least for non-tail calls). See also the discussion here: Recursive call vs while loop - #18 by stevengj
I think it’s a common myth that recursion is necessarily slow. The key trick that is usually missing from such discussions is to enlarge the base case.