GHC has a comparatively expensive write barrier. That could be seen as taking advantage of immutability. The runtime system takes more advantage of purity, though. For example, it's safe to have certain races, because they will give the same result. Too much work duplication must be avoided, though, but that's much cheaper than excessive locking.
Incidentally fusion won't work for this example (at least not as currently implemented.) Only `foldr` is fusable. Fortunately, regular inlining does just fine.
No, the problem with Itanium is/was that it's no-one knew how to write good compilers for them. Also, VLIW is incredibly close to the hardware and thus bound to be outdated very quickly. It didn't help that IA64 has about 40bits / instruction and thus the least instruction / byte of all the mainstream architectures.
Transmeta had the right idea, they did the x86-to-VLIW dynamically at runtime and in software, coupled with proper code caches. But it seems they were to early -- the market wasn't ready for them.
JIT simply stands for Just-in-time compilation or optimisation, that is you delay optimising parts of your program until the program executes.
Most systems start of by interpreting the some form of bytecode and observe which parts are executed most frequently and then compile these to machine code.
Now there are two main techniques for choosing what to compile: You can count how often each function/method is executed and then compile a method to machine code when the counter reaches a certain threshold. You can even compile several versions of the function, e.g., one for integers, one for floating point values.
A tracing JIT, on the other hand, does not compile single methods at once, but whole execution paths (traces) throughout the whole program (typically some kind of loop). A trace can span several methods and thus may allow better inter-procedural optimisations. For example if a certain if-branch is taken more frequently the trace will only include the common case and will fall back to the interpreter in the uncommon case.
Tracing JIT is probably more complicated than method-based JIT, because there are issues with code duplication (e.g., several traces may include the same code) and trace exits can be expensive, so you have to efficiently link traces together dynamically. However, trace-based JIT allows more agressive optimisations and, since traces tend to be longer than methods allow more optimisations in general.
A closure is a function + environment, i.e., the values of its free variables at the time of creation.
A thunk is a suspended computation together with an environment. That is, a thunk is a special type of closure, where the function takes no arguments. Additionally, a thunk is updated with its result.
In GHC's execution model, the Spineless Tagless G-machine (STG), every heap object is actually a closure. That is, every heap object has a code pointer that can be called to return the object's value. For evaluated objects (including functions) that is essentially the identity function, for thunks this will evaluate as much as necessary, return, and possibly update it's heap object with its new value. Of course, there are several optimisations going on, but that's the basic idea.