A shell script written in a procedural style (e.g. with Bash or equivalent 
shell language) will frequently start out by declaring some global 
variables, then perform some conditional checks (if then else), throw in a 
few loops (for, while), and ultimately end up with some new values in those 
initially declared variables that you use as your program's output. If you 
are feeling particularly intrepid, you might factor out some repetitive 
operations into separate subroutines defined higher up in the file and then 
call them as necessary in those aforementioned conditional blocks and loops.

The mental model behind this type of programming is the Universal Turing 
Machine. Your variables are some internal state that the program 
instructions are reading and writing, reading and writing, writing and 
reading until you get to the last instruction that returns some subset of 
the final variable values. The driving principle is that computation is 
accomplished by the free mutation of state.

A program written in a functional style (e.g. with any Lisp, one of the 
MLs, Haskell, Clean, etc) begins with a chunk of data, which may either be 
hard-coded, input from the outside world, or generated internally with a 
function like "range" or "rand". This piece of data may or may not be 
stored in one or more global variables. However (and this is HUGE however), 
these are not generally mutated over the life of the program. That is to 
say, they are constants. More often than not, you won't even store the 
initial data in a global variable but will just feed it into a function 
that processes it and spits out some new data, which is then fed to another 
function that performs some other processing operation and again spits out 
some new data. Ultimately, the data that you produce is passed through an 
arbitrarily long pipeline of functions until the final result is produced 
and returned by the program. In practice, these function calls are rarely 
linear and are much more likely to form a branching call tree. But 
ultimately, the relevant branches of this tree will be traversed and 
executed in a depth first manner (unless lazy evaluation inserts its magic 
to reorder some of that computation behind the scenes) and you still get to 
a final output returned by the last function fall evaluated.

The mental model behind this type of programming is Lambda Calculus. There 
is no mutable state anywhere in a pure functional program, and instead the 
intermediate results are passed through the call stack from function to 
function. In practice, because stack sizes are limited, most values passed 
between functions will be boxed references pointing to memory locations on 
the heap. However, as far as the functional programmer is concerned, there 
is no heap and there is no mutable state. Immutable values simply flow 
seamlessly from one function to the next until the program has finished. 
The driving principle is that computation is accomplished by transforming 
data through mapping a set of immutable inputs to an immutable output. 
Think f(x,y) = z.

In order to accomplish this stateless pipeline of data transformations, 
functions much possess a property called "referential transparency". This 
means that a function must be able to calculate its outputs 
deterministically using only its inputs AND without modifying any of its 
inputs in the process. This property is preserved even when global 
variables are referenced within the body of a function as long as the 
values associated with those global variables are constants throughout the 
lifetime of the program.

In practice, very few languages (outside of Haskell) actually try to be 
completely pure and allow no mutation of state whatsoever. Clojure opts to 
make all data immutable by default, but provides some special language 
features (refs, atoms, agents, volatiles) that you can use if you really do 
want to write an algorithm that involves some mutable state. Unless there 
is a performance bottleneck (as in numerical computing) or no other 
sensible way to model the domain (as in some web applications), making use 
of these features for mutable state is generally frowned upon. When they 
are really necessary and valuable, however, Clojure's language tools for 
accomplishing mutation are wonderful because they carefully protect it 
against concurrency clashes in multi-threated environments.

To approach writing a functional program, first think about how to model 
the computation as a series of data transformations. Then build your 
program from the bottom up (prototyping and testing it in the REPL) by 
writing small, referentially transparent functions that describe the lower 
level operations that you will need. Then build higher level functions on 
top of those that build up your abstractions in a layer cake style until 
you reach your entry point function that either takes input data from the 
outside world or generates it internally and then starts the execution of 
the function call tree.

This is, of course, my attempt at summarizing the mindset that I frequently 
use when trying to write a new functional program, and I welcome additions 
or corrections from other folks in this thread to further flesh it out. 
Best of luck in your functional programming adventures and with Clojure in 
particular. The FP way of thinking can be a bit tricky to wrap your mind 
around when coming from a traditional OOP background, but once you grok it, 
there is a great feeling of freedom and simplicity that emerges from what I 
suspect many of my fellow Clojurians would agree is a far more elegant and 
powerful programming paradigm.

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to