I tried to create factorial function in Clojure using recursion
(defn fac[x] (if (= x 1) 1 (* x (fac (- x 1)))))
Now, when I try to call the function
(fac 5)
I get the exception
java.lang.IllegalStateException: Attempting to call unbound fn: #'sandbox11355/fac
Does that mean it isn't possible to use recursion when defining functions with defn
keyword?
Also, how would I embrace this functional syntax most efficiently, as I'm used to imperative/OOP way of thinking? It feels just awkward to type everything in the reverse order. With procedural paradigm, the continuum of thoughts maps directly to new line of code which mutates the value. With functional syntax, for each step to manipulate the current value, I have to wrap new function around the expression, and it's hard to keep track of parens and scopes. Should I learn to think the procedural model in reverse order to fluently code in functional style?
I understand the benefits of no mutable state and pure functions (less bugs), but it's hard to believe it's worth of losing the ease of writing procedural code. For now, all this seems over-hyped unorganized mess, but maybe it starts making sense.
Some info on your concern about functional and procedural programming follows. It's not particularly original, but maybe it will get you started on how to think about this new stuff.
Functional programming is not procedural programming in reverse. It's a higher level of abstraction, and most everything we interact with can be seen as an abstraction; otherwise, we would never get anything useful done because we'd be so concerned with the minutiae of every little thing we deal with. Likewise, all code, in any language, eventually becomes a series of instructions to the CPU, and these instructions are the epitome of "imperative" or "procedural." The question becomes, "How much control do I need over the extremely low details in order to solve my problem?"
One way to add some numbers together, being pretty explicit (just pseudocode, hopefully the intent is clear):
int nums[10] = {0,1,2,3,4,5,6,7,8,9};
int i = 0;
int acc = 0;
start_loop:
if (i >= 10) goto done_loop;
int num_address = (nums + i);
int num_value = *num_address;
acc = acc + num_value;
i = i + 1;
goto start_loop;
done_loop:
return acc;
It is tedious, but not as tedious as assembly code. To abstract out some of the details of looping, C/java/etc provide a control structure called a for
loop:
int nums[10] = {0,1,2,3,4,5,6,7,8,9};
int acc = 0;
for (int i = 0; i < 10; i++)
acc += nums[i];
return acc;
This seems perfectly normal, of course, when you write imperative code on a regular basis. You think iteratively, and about the details of how to access the array at each offset from its base. However, this can also be thought of as tedious. Why should I care about the details of how to access each member of the array? A further abstraction that any functional languages provides is called reduce
. Think of it as a tool provided in a similar way that for
is provided to C/java/etc programmers. It looks strange, much in the same way the syntax of for
would look to assembly programmers seeing it for the first time:
(reduce + (range 10))
So all we're doing here is abstracting out details of the loop to the point that we really don't think much about the loop that's actually occurring. We also abstract out the details of creating an explicit range of numbers, and just say "give me the integers from 0 (inclusive) to 10 (exclusive)". It's just abstracting out details. The result is generally an ability to focus more on the problem at hand.
For adding numbers, or thinking higher level, a functional way of programming generally allows us to be more productive, with less code, while we let the various levels of compilers handle the messy details for us. If the problem is very low level, however, then we may desire constructs in the language that are a better fit for our problem. The key is always using the right tool for the right job.
Of course, it's not a perfect world, and often in clojure we are forced to write low level details dealing with bits and bytes, synchronizing concurrent code, looping, and so on. But generally, being declarative and stating what you want to do, rather than being more explicit about how to do it, has many benefits.
Do the 4clojure problems, give it a month or two to start really making sense, and allow your mind to make the shift from mutating variables to evaluating expressions. There's a very high probability that you will enjoy it very much, and the worst that can happen is that you can broaden your horizons. Good luck and have fun!