You thinking starts off in the right direction as it is true that IO monads allow for the separation of impure and pure actions. I Think you are missing something when you mention that
Also, I am aware that IO allows for referential transparency.
You might be mixing up the semantics of the IO itself with the benefits that they provide to your code outside of it. The IO doesn't so much allow for referential transparency in and of itself but instead, allows for functions that are referentially transparent (pure) to interface with the impure actions contained within the IO without losing their referential transparency.
When you compose an IO action and run it, the results are not guaranteed to be referentially transparent and that is exactly the point. This is not surprising when we are armed with the understanding that IO is the dumping ground for impure actions.
So, when this lazy composition is finally invoked no other concurrent
code can distort that (this lazy composition).
Unfortunately, it isn't quite so simple. Code being lazy doesn't make it referentially transparent. The evaluation of a chain of functions such as an IO being delayed (lazy), has no bearing on the values upon which they are operating on.
In the context of javascript, all values are references. Any function on the stack that has within its scope, a reference, can change the value of said reference. Let me show you what this means for your own example code showing the IO returning the value of 1100 no matter what. When I change the value of x before or after declaring the IO wrapping x, the value returned by the IO will reflect such a change. Therefore this code won't return //1100 no matter what
but will infact, return 1200
.
function switcharoo() {
x = 2;
}
var x = 1;
var IOdoMaths = IO(() => x).map(x => x * 100).map(x => x + 1000);
// Some concurrent proccess calls this
switcharoo();
var result = IOdoMaths.runIO();
console.log(result); // 1200
We were expecting the IO action to result in 1100
, but istead we got 1200
. Remember, when we created our IO, we wraped a reference and not a value. This holds true for any type of closure in Javascript. it doesn't matter if you stuff a closure in an IO after you declare it, the same rules are going to apply.