Parsing infix expressions in Clojure
First, yes, this is functional, and yes, this is how to use loop properly.


  . . . using recur on every loop iteration instead of mutating variables.


This is the intent of loop. Instead of mutating a variable, you just pass the "altered data" to the next iteration via recur.

You also aren't abusing side effects by misusing def or atoms, so that's good.



My main concern with this code is how it's really just one giant function. There's no function names indicating what certain code is doing, and no comments noting the significance of any lines. Now, I'm personally a stickler for breaking code down into functions, but it's generally regard as best practice as well. As this answer notes (ignoring the mention of "imperative"):


  Having a large imperative function that conveys many ideas is hard to digest and reuse.


I think the size of this function does make it hard to digest.

Just as an extreme counter example, here's the same algorithm that I wrote a year ago*. It's almost 4x longer as yours, but it's also much clearer what each bit of code is doing (and has ~10 lines dedicated to documentation). Code like

(-&gt; equation
    (tokenize-equation)
    (infix-&gt;RPN-tokens op-attr-map)
    (tokens-to-string)) ; Should arguably be called token-&gt;string


makes it fairly clear what it's responsible for, even without taking into consideration the function name that it's in.

I'm won't try to break your code down (mostly because I burned my hand pretty bad, and typing this is hard enough), but if you take a look at my example, you may be able to find some inspiration and strike a middle ground for granularity.

Good luck



*  Although my version doesn't evaluate the expression since I'm translating it into a String and in my example, the data isn't available at compile-time.