mdgriffith / elm-optimize-level-2

BSD 3-Clause "New" or "Revised" License
129 stars 14 forks source link

Lambdaify function composition #73

Open jfmengels opened 2 years ago

jfmengels commented 2 years ago

This PR is based off of #63 (and until it gets merged, contains those same commits).

This transformer turns function composition into an anonymous function with a named parameter.

-- Elm
f1 >> f2
-- Without eol2
A2($elm$core$Basics$composeR, f1, f2);
-- With this transformation
var fn = function (_param_1) { return f2(f1(_param_1)); };

It makes https://github.com/mdgriffith/elm-optimize-level-2/pull/65 redundant because this transformation leads that function to have the exact same code as the transformation in that PR. I think that PR does help see the performance increases this can give.

Asset-size wise: I measured it on a single small application, and the size of the compiled JS code is reduced compare to without the transformation. But the gain gets reduced after minification and is reduced to 0 after gzip. But at least it didn't get worse!

There were a lot of cases where I try to make sure the performance doesn't get worse than what we currently have because of how the components get called, but in some cases I made it even faster.

I hopefully wrote a clear documentation of the transformation in the code, but I think the tests go a bit further detail, so do check out both.

Further work

There are still some improvements that can be done but I'll get to that later. For instance improving the following:

-- Given f1 = F2(...) 

-- Elm
f1 x >> f2
-- Without eol2
A2($elm$core$Basics$composeR, f1(x), f2);
-- With this transformation
var _decl_1 = f1(x),
        fn = function (_param_1) { return f2(_decl_1(_param_1)); };
-- Ideally
var fn = function (_param_1) { return f2(A2(f1, x, _param_1)); };

I already do this when encountering A3 expressions, so it's do-able, but a bit more work. The current solution is already an improvement in all cases (because it's pretty conservative) but we could go a step further when we know better.

Another one would be to change not calls into !, which would remove the cost of a function call. That said, I tried benchmarking the same idea for field accesses, and that didn't improve anything, so maybe this would not change much either.


I am welcome to suggestions and improvements (including but not limited to the name of the transform :grin: )

Also, please let me know how I can properly benchmark the results of this transformation and what the next steps should be before we merge it in.