purescript-st icon indicating copy to clipboard operation
purescript-st copied to clipboard

Inline optimization with `run` still makes calls to `modify`

Open milesfrain opened this issue 5 years ago • 1 comments

Originally reported here.

There's an unexpected call to Control_Monad_ST_Internal.modify in the JS output of the following function:

simulate :: Number -> Number -> Int -> Number
simulate x0 v0 time =
  run do
    ref <- new { x: x0, v: v0 }
    for 0 (time * 1000) \_ ->
      modify
        ( \o ->
            { v: o.v - 9.81 * 0.001
            , x: o.x + o.v * 0.001
            }
        )
        ref
    final <- read ref
    pure final.x
var ref = { value: { x: x0, v: v0 } };                
 
Control_Monad_ST_Internal["for"](0)(time * 1000 | 0)(function (v) {                
  return Control_Monad_ST_Internal.modify(function (o) {                  
    return {                    
      v: o.v - 9.81 * 1.0e-3,                      
      x: o.x + o.v * 1.0e-3                      
    };                    
  })(ref);                  
})();

The original version of the book shows a more optimized JS output that does not make a call to modify:

var ref = { x: x0, v: v0 };     
                                  
Control_Monad_Eff.forE(0)(time * 1000 | 0)(function (i) {    
  return function __do() {    
    ref = (function (o) {    
      return {                                                                         
        v: o.v - 9.81 * 1.0e-3,                                               
        x: o.x + o.v * 1.0e-3    
      };    
    })(ref);    
    return Prelude.unit;    
  };    
})();    
                                       
return ref.x;

milesfrain avatar Apr 25 '20 20:04 milesfrain

Just to add my notes on this. This is what simulate looks like w/o inlining, plus the extra write for comparison:

var simulate = function (x0) {
    return function (v0) {
        return function (time) {
            return Control_Monad_ST_Internal.run(function __do() {
                var ref = Control_Monad_ST_Internal["new"]({
                    x: x0,
                    v: v0
                })();
                Control_Monad_ST_Internal["for"](0)(time * 1000 | 0)(function (v) {
                    return Control_Monad_ST_Internal.modify(function (o) {
                        return {
                            v: o.v - 9.81 * 1.0e-3,
                            x: o.x + o.v * 1.0e-3
                        };
                    })(ref);
                })();
                var $$final = Control_Monad_ST_Internal.read(ref)();
                Control_Monad_ST_Internal.write({
                    x: -1.0,
                    v: -1.0
                })(ref)();
                return $$final.x;
            });
        };
    };
};

~~What's interesting here is that all of the foreign functions (new, for, read, write) have an extra abstraction, while modify doesn't, because it's defined in PS. The ST optimizer in its current (unchanged) state accounts for when modify was still a foreign function, thus explaining the failures. I suppose the reasonable action would be to make a special case in the optimizer for modify.~~ This can be resolved by having MagicDo account for the code these utilities generate.

sjpgarcia avatar Dec 30 '21 17:12 sjpgarcia