First a little tangent. I love Haskell's concurrency system. Well, basically it has three of them. The first is the only one where you explicitly fork off threads, usually called simply "concurrency". Because of purity the only way you can fuck up another thread is if you explicitly hand it a mutable reference you're modifying without locking. You can safely hand it a pure value, because that can't be modified anyway; the worst that can happen is that two threads evaluate it at the same time (there's a 1-instruction window where that can happen), but even then the only consequence is doing the same work twice, as both threads will replace the thunk with the same data.
Then there's MVars, which used to be the only concurrency primitive before STM. MVars are nifty, because they're like locks with values. They're either empty or full, and there's blocking and non-blocking operations (the non-blocking operations don't circumvent the lock, they just do nothing, and tells you so, where the blocking operations would've failed). It's still possible to deadlock, and it's best not to share MVars between too many threads, or things could get compliated.
The last addition to haskell concurrency is called STM, which is short for Software Transactional Memory. The basic premise is that you've got TVars (which are like MVars, except they're never empty), a few operations on them (read and write, basically), a few operations on those operations (orElse), and finally a stand-alone operation called retry. These operations are all monadic, so they can be sequenced and contain pure values. Now, every STM operation guarantees that either all reads and writes complete without interference from another thread, or none of them do. When composed, this guarantee carries through to the entire resulting operation, making for easily composable atomic operations. For example, it's not hard to imagine a TMVar (in fact, such a thing already exists. It's simply a TVar (Maybe a)). Just like MVars, these can be used for locks. What you can't do with regular MVars, however, is take several locks at once without caring about which order you take them in, or releasing them in the correct order if you failed to take them all. Neither can you arbitrarily compose two lock-taking operations without caring for exactly what they're doing.
The second concurrency system in haskell is called "parallellism". The difference between it and concurrency is that a concurrent program does several things at the same point in time, like handling keyboard events while drawing things on the screen. A parallell program does one thing, but uses several threads to do it; they all pull together in the same direction towards a common goal. The nice thing about parallellism is that it's not important which thread does what and in what sequence. Therefore, instead of spawning threads, you instead annotate your code in places where parallell execution might be a good idea. "Here I need to know the values of both x and y, and they both take some time to compute, so it might be a good idea to compute y in parallell while I do x, and then later we can add them together." The runtime has a collection of these sparks, and whenever a worker thread is free it picks up one of these sparks and evaluates it. If the main thread requires a sparked but unevaluated value, it'll just evaluate the spark itself like it normally would in a non-parallell program.
Parallellism removes the need to worry about threads and locks or any other concurrency mechanizm. Instead, they'll only have to worry about which values would be a good idea to evaluate in parallell and making sure things don't get too fine-grained, or the usually small overhead will grow larger than the gains you get from evaluating multiple things at once.
Parallellism is also somewhat limited in that it can only evaluate pure values that have no side-effects. Anything else would be madness, as you want control of the sequencing of side-effectful code. One of the great things about parallellism is that a working program will never break if you add parallellism to it; it can only become slower, but will always return the same result.
The last concurrency system is in my opinion even niftier: Nested data parallellism. Data parallellism already exists; some FORTRAN dialects have it, and possibly others too. The idea here is that it's possible for a compiler to look at a sequential algorithm and automatically derive parallellism in such a way that all threads are at maximum utilization at all times, including concerns such as cache-performance, locality and whatnot. Typically this has been restricted to single-level loops over arrays using a restricted subset of operations usually available, and writing those algorithms has usually been hard.
Then some guys threw a bunch of maths together and developed nested data-parallellism, which is a way of converting nested loops into a flat one, combining and transforming the operations along the way. This makes writing parallell algorithms almost as easy as writing sequential ones, and they'll most likely perform a lot better than any hand-tuned assembly will. It also helps that Haskell's restricted subset of operations is the entire set of pure functions (as long as they're correctly types. There's currently restrictions on the type of the vector elements); because purity is what's needed to convert those sequential algorithms implicitly into parallell ones, and there's no way to prove something's pure in FORTRAN, developers are stuck with whatever's there already, unable to write their own abstractions. In Haskell, proving purity is simple, and you can do pretty much whatever you want.
GHC got some alpha-stage experimental support for this in its 6.10 release. Last I heard, it was a crapload harder to implement than they'd first imagined, but they were starting to get on top of things, so while 6.12 still won't have full support it's quite possible 6.14 or 6.16 will.
Now then, back to flaming.
EduardoLeon wrote:However, since state is a very real thing, it must be simulated when you do functional programming.
Not very zen of you. If you can simulate state, you have state.
EduardoLeon wrote:And, since not any there's not any decent alternative, state must be expressed as a function parameter. And, since you can't fill the stack so much (yes, parameters fill the call stack!!!)
Where do you think your loop varaible's located? If you say "in a register" I'll retort by saying that's where functional programs put their parameters.
EduardoLeon wrote:you have to resort to contrived things such as tail call optimization
I like to call that tail call pesimization. Tail call optimization is doing just what you'd do yourself in assembly anyway: Instead of calling another function only to return as soon as it returned to you, you simply jump to it, letting it return to your caller instead of you. This goes for all functions, not just self-recursive ones, and if people have gotten used to otherwise it's only because compiler-writers are lazy slobs.
EduardoLeon wrote:But I guess that transforming a general recursive program into a general iterative program is an undecidable task. (I don't know, really.) Only if it's a decidable task and it can be done in a reasonable time, then functional programming might be worth a try. I mean, functional programming using a computer.
As Xanthir said, every recursive program can be transformed into an equivalent iterative one. How else did you think compilers for functional programs even work? Some recursive algorithms require a stack when transformed into iterative ones; the compiler usually uses the one that's already there. You know, the call stack... I'd do the same if I were hand-coding this in assembly, instead of bothering about allocating a new stack somewhere else.
EduardoLeon wrote:I manually transform the recursive definition into an iterative sequence of instructions that evaluates the function.
That's pretty dumb when any decent compiler would do the exact same thing for you if you enable optimizations.
It is practically impossible to teach good programming to students who are motivated by money: As potential programmers they are mentally mutilated beyond hope of regeneration.