Manipulate nodejs global effectfully vs. not

For some reason, I needed to manipulate the nodejs globalThis through FFI:

export const initGlobal = function() {
  globalThis.shenanigan = 'shenanigan'
}

When being exposed with foreign import initGlobal :: Effect Unit, it works. However, if foreign import initGlobal :: Unit, it doesn’t have the expected effect! The Effect monad really makes a difference here.

This is most amazing to me. How the heck purescript compiler/esbuild/etc. know the semantic of this? I can’t tell the difference from generated main.js either.

a Effect is more or less (after compilation) just a something wrapped into a function () { ... something .. } - so the effect hast to be called initGlobal() in order to express the effect - if you import it as Unit and use this it will probably just plug a reference to this function somewhere (maybe there is even a optimizer step that just removes the hole step as it is Unit - don’t know to be honest) but will not call it.

You should be able to see this in the generated code.

2 Likes

It is more complicated than that. For the non-effectful version, let _ = initGlobal did get optimized away in the final bundled main.js; it was totally absent so that was understandable.

Then I went back to compare the Effect String vs. String version, both had the initGlobal present in the final main.js. BUT … you guessed it, one with the desired side effect and one without. It is magic :sweat_smile:

Can you maybe post the part with the generated code? I’m pretty sure the String one will not be “called” - if you do let _ = initGlobal it should not be put through the bind part where the delayed call should be performed (_ <- initGlobal would do that but this should result in an type error)

1 Like

You are right. The difference in the generated code was:

var main = function __do() {
  bind4(initGlobal)(log2)();
  return launchAff_(mainBody)();
};

vs.

var main = function __do() {
  log2(initGlobal)();
  return launchAff_(mainBody)();
};

What did the bind4 do?

it should call initGlobal() and pass the result to log2 - you can probably find the definition in your generated code.