I'm really interested in becoming proficient in Clojure/Clojurescript for web applications. Right now I'm making simple command-line applications to get a feel for the language.
But right now it's hard to grasp the workflow of how to get things done in a language without mutable variables.
My problem is: I'm making a little RPN Calculator, in which the user can enter numbers to add to a stack and also do math operations on the stack:
> ;adding to stack
> 4 4
> ; print the stack
> [4, 4]
> 2 3
> p
> [4 4 2 3]
> ; adding the top two items to the stack
> +
> p
> [4 4 5]
> + -
> p
> [-5]
So my problem is how to keep track of the stack, if there are no variables. I wrote this in Java first using the Java stack, and obviously in Clojure it's going to be a much different approach, but I'm not quite sure the way to approach the problem.
While it works, I'd avoid the atom/ref approach, as these are not really the "Clojure way". (They are the correct tools for some problems, but not this one). The stack-frame approach is much more in line with Clojure philosophy.
To expand on the stack-frame solution, the application of each operation would be a function like:
new-stack (apply-op stack op)
Operations like + and - will do the obvious, while 'p' will have a side-effect (IO) but otherwise return the original stack.
The loop is then trivial:
(loop [stack [] op (get-op!)]
(if (not= 'q op)
(recur (apply-op stack op) (get-op!))))
I'm presuming a symbol of 'q' as the termination command, and get-op! to be the reading operation.
Also worth noting is that the "stack" is more naturally implemented with a list, as the required operations of first/rest/cons are already available. For instance, applying any binary operator to a list-based stack is just:
(cons (the-operator (first stack) (second stack)) (rest (rest stack)))
Or using destructuring for clarity:
(let [[a b & r] stack] (cons (the-operator a b) r))
Using a vector as a stack is not so easy, nor efficient.