It's not the order of
application that is irrelevant, it's the order of
evaluation, due to the Church-Rosser Theorem.
For example, consider the expression:
myList = map(double, [10, 20, 30])
We can evaluate the "map" call, to get this:
myList = [double(10), double(20), double(30)]
Which of these calls to "double" should we evaluate first? It doesn't matter! We can go left-to-right:
myList = [20, double(20), double(30)]
myList = [20, 40, double(30)]
myList = [20, 40, 60]
We can go right-to-left:
myList = [double(10), double(20), 60]
myList = [double(10), 40, 60]
myList = [20, 40, 60]
Or any other order, and we will converge to the same result: [20, 40, 60]. If a language has the Church-Rosser property (AKA being "confluent"), then we're guaranteed that if we reach a result (i.e. if we don't get stuck in an infinite loop) then we get
the same result, regardless of evaluation order.
To see the link to parallelism, imagine that the above list is much larger, and rather than just doubling a number we're doing some expensive calculation. In a non-confluent language, we'd have to specifically designate the tasks which can be performed in parallel, e.g. creating a job queue or the like, to avoid race conditions and out-of-order side-effects. In a confluent language, everything can be evaluated in parallel without affecting the result; the only thing we have to care about is performance (the overhead of parallelising small tasks far exceeds the gains) and data dependencies (if X uses the result of Y, there's no point starting X until Y has finished; and it's better to run both on the same machine, to minimise network usage).
Now instead of a list of numbers separated by ",", imagine we have a list of statements separated by ";":
myProcedure = print(reverse(" olleh")); print(concat("wo", "rld"))
If the "print" procedure has side-effects, we get
different results depending on which order we evaluate the calls in, hence the language would be non-confluent and we would have to be very careful that parallelism doesn't break the functionality.
On the other hand, we could approach these statements from a higher level view: we can think of each statement as a perfectly normal piece of data, e.g. as if it were a node in some AST. In which case, the ";" separator is just a pure function for combining two statements (pieces of data) into one statement (piece of data). In this setting, we can regain confluence, and hence evaluate in any way we like, e.g. we can reduce the inner expressions to:
myProcedure = print("hello "); print("world")
If we eventually choose to execute this "AST", all of the side-effects will still occur in the same order, even if we've evaluated a bunch of things inside it, since evaluating a compound statement like 'a; b' cannot change the order of the parts, in the same way that evaluating a list '[x, y]' cannot change the order of its elements. Treating side-effecting statements as pieces of data in this way lets us blur the lines between interpreters, compilers and "eval", and is similar to what Haskell's "IO" type does.
In a non-confluent language we couldn't evaluate parts of the second statement in general, e.g. because "concat" might depend on effects from "reverse" or the first call to "print", hence there is less opportunity for aggressive optimisation, and without a distinction between pure and impure code (like we have above between functions and 'statement data'), there's no way in general to know which parts can be evaluated in parallel.
In fact, confluence isn't limited to function application (AKA "beta reduction"); we can add all sorts of program transformation rules to our language whilst remaining confluent (although such rule-adding mechanisms can be used to break confluence, this is usually considered user-error). For example, we might have a rule like the following:
print(x); print(y) --> print(concat(x, y))
This turns one statement value into another with equivalent behaviour, but better performance (e.g. fewer system calls, etc.). Haskell's GHC compiler allows these sorts of rules, although they're applied at compile-time rather than run-time. There are also languages based around rewriting expressions at run-time, like Pure and Maude, for which all of these arguments also apply.