src/index.js:
import { max } from "fp-ts/es6/Ord";
console.log(max);
rollup.config.js:
import resolve from "@rollup/plugin-node-resolve";
export default {
input: "src/index.js",
output: {
file: "target/rollup.js",
format: "cjs"
},
plugins: [resolve()]
};
package.json:
{
"dependencies": {
"fp-ts": "^2.0.0",
"@rollup/plugin-node-resolve": "^7.0.0",
"rollup": "^1.29.0"
}
}
Then run rollup -c rollup.config.js.
The output of rollup should not include lots of unused code:
'use strict';
/**
* The `Ord` type class represents types which support comparisons with a _total order_.
*
* Instances should satisfy the laws of total orderings:
*
* 1. Reflexivity: `S.compare(a, a) <= 0`
* 2. Antisymmetry: if `S.compare(a, b) <= 0` and `S.compare(b, a) <= 0` then `a <-> b`
* 3. Transitivity: if `S.compare(a, b) <= 0` and `S.compare(b, c) <= 0` then `S.compare(a, c) <= 0`
*
* See [Getting started with fp-ts: Ord](https://dev.to/gcanti/getting-started-with-fp-ts-ord-5f1e)
*
* @since 2.0.0
*/
/**
* Take the maximum of two values. If they are considered equal, the first argument is chosen
*
* @since 2.0.0
*/
function max(O) {
return function (x, y) { return (O.compare(x, y) === -1 ? y : x); };
}
console.log(max);
The output of rollup includes lots of unused code:
Show code snippet
'use strict';
/**
* @since 2.0.0
*/
/**
* @since 2.0.0
*/
function identity(a) {
return a;
}
var isFunctor = function (I) { return typeof I.map === 'function'; };
var isContravariant = function (I) { return typeof I.contramap === 'function'; };
var isFunctorWithIndex = function (I) { return typeof I.mapWithIndex === 'function'; };
var isApply = function (I) { return typeof I.ap === 'function'; };
var isChain = function (I) { return typeof I.chain === 'function'; };
var isBifunctor = function (I) { return typeof I.bimap === 'function'; };
var isExtend = function (I) { return typeof I.extend === 'function'; };
var isFoldable = function (I) { return typeof I.reduce === 'function'; };
var isFoldableWithIndex = function (I) { return typeof I.reduceWithIndex === 'function'; };
var isAlt = function (I) { return typeof I.alt === 'function'; };
var isCompactable = function (I) { return typeof I.compact === 'function'; };
var isFilterable = function (I) { return typeof I.filter === 'function'; };
var isFilterableWithIndex = function (I) {
return typeof I.filterWithIndex === 'function';
};
var isProfunctor = function (I) { return typeof I.promap === 'function'; };
var isSemigroupoid = function (I) { return typeof I.compose === 'function'; };
var isMonadThrow = function (I) { return typeof I.throwError === 'function'; };
function pipeable(I) {
var r = {};
if (isFunctor(I)) {
var map = function (f) { return function (fa) { return I.map(fa, f); }; };
r.map = map;
}
if (isContravariant(I)) {
var contramap = function (f) { return function (fa) { return I.contramap(fa, f); }; };
r.contramap = contramap;
}
if (isFunctorWithIndex(I)) {
var mapWithIndex = function (f) { return function (fa) { return I.mapWithIndex(fa, f); }; };
r.mapWithIndex = mapWithIndex;
}
if (isApply(I)) {
var ap = function (fa) { return function (fab) { return I.ap(fab, fa); }; };
var apFirst = function (fb) { return function (fa) {
return I.ap(I.map(fa, function (a) { return function () { return a; }; }), fb);
}; };
r.ap = ap;
r.apFirst = apFirst;
r.apSecond = function (fb) { return function (fa) {
return I.ap(I.map(fa, function () { return function (b) { return b; }; }), fb);
}; };
}
if (isChain(I)) {
var chain = function (f) { return function (ma) { return I.chain(ma, f); }; };
var chainFirst = function (f) { return function (ma) { return I.chain(ma, function (a) { return I.map(f(a), function () { return a; }); }); }; };
var flatten = function (mma) { return I.chain(mma, identity); };
r.chain = chain;
r.chainFirst = chainFirst;
r.flatten = flatten;
}
if (isBifunctor(I)) {
var bimap = function (f, g) { return function (fa) { return I.bimap(fa, f, g); }; };
var mapLeft = function (f) { return function (fa) { return I.mapLeft(fa, f); }; };
r.bimap = bimap;
r.mapLeft = mapLeft;
}
if (isExtend(I)) {
var extend = function (f) { return function (wa) { return I.extend(wa, f); }; };
var duplicate = function (wa) { return I.extend(wa, identity); };
r.extend = extend;
r.duplicate = duplicate;
}
if (isFoldable(I)) {
var reduce = function (b, f) { return function (fa) { return I.reduce(fa, b, f); }; };
var foldMap = function (M) {
var foldMapM = I.foldMap(M);
return function (f) { return function (fa) { return foldMapM(fa, f); }; };
};
var reduceRight = function (b, f) { return function (fa) { return I.reduceRight(fa, b, f); }; };
r.reduce = reduce;
r.foldMap = foldMap;
r.reduceRight = reduceRight;
}
if (isFoldableWithIndex(I)) {
var reduceWithIndex = function (b, f) { return function (fa) {
return I.reduceWithIndex(fa, b, f);
}; };
var foldMapWithIndex = function (M) {
var foldMapM = I.foldMapWithIndex(M);
return function (f) { return function (fa) { return foldMapM(fa, f); }; };
};
var reduceRightWithIndex = function (b, f) { return function (fa) {
return I.reduceRightWithIndex(fa, b, f);
}; };
r.reduceWithIndex = reduceWithIndex;
r.foldMapWithIndex = foldMapWithIndex;
r.reduceRightWithIndex = reduceRightWithIndex;
}
if (isAlt(I)) {
var alt = function (that) { return function (fa) { return I.alt(fa, that); }; };
r.alt = alt;
}
if (isCompactable(I)) {
r.compact = I.compact;
r.separate = I.separate;
}
if (isFilterable(I)) {
var filter = function (predicate) { return function (fa) {
return I.filter(fa, predicate);
}; };
var filterMap = function (f) { return function (fa) { return I.filterMap(fa, f); }; };
var partition = function (predicate) { return function (fa) {
return I.partition(fa, predicate);
}; };
var partitionMap = function (f) { return function (fa) { return I.partitionMap(fa, f); }; };
r.filter = filter;
r.filterMap = filterMap;
r.partition = partition;
r.partitionMap = partitionMap;
}
if (isFilterableWithIndex(I)) {
var filterWithIndex = function (predicateWithIndex) { return function (fa) { return I.filterWithIndex(fa, predicateWithIndex); }; };
var filterMapWithIndex = function (f) { return function (fa) {
return I.filterMapWithIndex(fa, f);
}; };
var partitionWithIndex = function (predicateWithIndex) { return function (fa) { return I.partitionWithIndex(fa, predicateWithIndex); }; };
var partitionMapWithIndex = function (f) { return function (fa) {
return I.partitionMapWithIndex(fa, f);
}; };
r.filterWithIndex = filterWithIndex;
r.filterMapWithIndex = filterMapWithIndex;
r.partitionWithIndex = partitionWithIndex;
r.partitionMapWithIndex = partitionMapWithIndex;
}
if (isProfunctor(I)) {
var promap = function (f, g) { return function (fa) { return I.promap(fa, f, g); }; };
r.promap = promap;
}
if (isSemigroupoid(I)) {
var compose = function (that) { return function (fa) { return I.compose(fa, that); }; };
r.compose = compose;
}
if (isMonadThrow(I)) {
var fromOption = function (onNone) { return function (ma) {
return ma._tag === 'None' ? I.throwError(onNone()) : I.of(ma.value);
}; };
var fromEither = function (ma) {
return ma._tag === 'Left' ? I.throwError(ma.left) : I.of(ma.right);
};
var fromPredicate = function (predicate, onFalse) { return function (a) { return (predicate(a) ? I.of(a) : I.throwError(onFalse(a))); }; };
var filterOrElse = function (predicate, onFalse) { return function (ma) { return I.chain(ma, function (a) { return (predicate(a) ? I.of(a) : I.throwError(onFalse(a))); }); }; };
r.fromOption = fromOption;
r.fromEither = fromEither;
r.fromPredicate = fromPredicate;
r.filterOrElse = filterOrElse;
}
return r;
}
/**
* The `Ord` type class represents types which support comparisons with a _total order_.
*
* Instances should satisfy the laws of total orderings:
*
* 1. Reflexivity: `S.compare(a, a) <= 0`
* 2. Antisymmetry: if `S.compare(a, b) <= 0` and `S.compare(b, a) <= 0` then `a <-> b`
* 3. Transitivity: if `S.compare(a, b) <= 0` and `S.compare(b, c) <= 0` then `S.compare(a, c) <= 0`
*
* See [Getting started with fp-ts: Ord](https://dev.to/gcanti/getting-started-with-fp-ts-ord-5f1e)
*
* @since 2.0.0
*/
/**
* @since 2.0.0
*/
var URI = 'Ord';
// default compare for primitive types
var compare = function (x, y) {
return x < y ? -1 : x > y ? 1 : 0;
};
function strictEqual(a, b) {
return a === b;
}
/**
* @since 2.0.0
*/
var ordNumber = {
equals: strictEqual,
compare: compare
};
/**
* Take the maximum of two values. If they are considered equal, the first argument is chosen
*
* @since 2.0.0
*/
function max(O) {
return function (x, y) { return (O.compare(x, y) === -1 ? y : x); };
}
/**
* @since 2.0.0
*/
function fromCompare(compare) {
var optimizedCompare = function (x, y) { return (x === y ? 0 : compare(x, y)); };
return {
equals: function (x, y) { return optimizedCompare(x, y) === 0; },
compare: optimizedCompare
};
}
/**
* @since 2.0.0
*/
var ord = {
URI: URI,
contramap: function (fa, f) { return fromCompare(function (x, y) { return fa.compare(f(x), f(y)); }); }
};
var contramap = pipeable(ord).contramap;
/**
* @since 2.0.0
*/
var ordDate = ord.contramap(ordNumber, function (date) { return date.valueOf(); });
console.log(max);
Add annotations/comments to indicate no side effects, e.g. in fp-ts/es6/Ord.js:
-var contramap = pipeable(ord).contramap;
+var pipeableResult = /*#__PURE__*/pipeable(ord);
+var contramap = pipeableResult.contramap;
export {
/**
* @since 2.0.0
*/
contramap };
/**
* @since 2.0.0
*/
-export var ordDate = ord.contramap(ordNumber, function (date) { return date.valueOf(); });
+export var ordDate = /*#__PURE__*/ord.contramap(ordNumber, function (date) { return date.valueOf(); });
This fixes the issue when using Rollup. Reduced test case.
Note this does not fix the issue with webpack, for some reason: https://github.com/webpack/webpack/issues/10253
Which versions of fp-ts are affected by this issue? Did this work in previous versions of fp-ts?
| Software | Version(s) |
| ---------- | ---------- |
| fp-ts | See above |
| TypeScript | See above |
Keep in mind that this:
-var contramap = pipeable(ord).contramap;
+var pipeableResult = /*#__PURE__*/pipeable(ord);
+var contramap = pipeableResult.contramap;
won't really help much, because #__PURE__ only works on call & new expressions and you access a property later which will cause a bailout on this case (because a getter is not known to be a side-effect free). I've proposed extending #__PURE__ with getters support, but it has been declined by Terser maintainers.
Note this does not fix the issue with webpack, for some reason: webpack/webpack#10253
I discovered that this is because webpack is not as aggressive as Rollup: https://github.com/webpack/webpack/issues/10253#issuecomment-574676015
In turn, this is because Terser does not respect "pure" comments on getters: https://github.com/terser/terser/issues/513. (As pointed out by @Andarist above.)
Therefore we would need to do this:
diff --git a/node_modules/fp-ts/es6/Ord.js b/node_modules/fp-ts/es6/Ord.js
index c6d925d..438bf26 100644
--- a/node_modules/fp-ts/es6/Ord.js
+++ b/node_modules/fp-ts/es6/Ord.js
@@ -137,8 +137,8 @@ export function getSemigroup() {
}
const M = {
// tslint:disable-next-line: deprecation
- concat: getSemigroup().concat,
- empty: fromCompare(() => 0)
+ concat: /*#__PURE__*/(() => getSemigroup().concat)(),
+ empty: /*#__PURE__*/fromCompare(() => 0)
};
/**
* Returns a `Monoid` such that:
@@ -245,7 +245,8 @@ export const ord = {
URI,
contramap: (fa, f) => fromCompare((x, y) => fa.compare(f(x), f(y)))
};
-const { contramap } = pipeable(ord);
+const pipeableResult = /*#__PURE__*/pipeable(ord);
+const contramap = /*#__PURE__*/(() => pipeableResult.contramap)();
export {
/**
* @since 2.0.0
@@ -254,4 +255,4 @@ contramap };
/**
* @since 2.0.0
*/
-export const ordDate = ord.contramap(ordNumber, date => date.valueOf());
+export const ordDate = /*#__PURE__*/ord.contramap(ordNumber, date => date.valueOf());
Here is a reduced test case where you can see how tree shaking works in both Rollup and webpack: https://github.com/OliverJAsh/tree-shaking-test/tree/fp-ts-issue-1087.
If you run patch-package, it will apply the above patch to the fp-ts Node module. After that you will see tree shaking works significantly better.
@gcanti What do you think about making these changes?
This by @Andarist might help: https://github.com/Andarist/babel-plugin-annotate-pure-calls
What do you think about making these changes?
@OliverJAsh
- in a realistic usage of fp-ts, is there a tangible benefit? The pipeable module is used in almost all modules
Good question. I don't have an immediate answer, but I'll give it some thought. The reason I ended up going down this rabbit hole is because I noticed that we were bundling more code than we actually use, e.g. our bundle contains Ord and Show even though we never use these directly nor indirectly (e.g. via methods which depend on them).
in a realistic usage of fp-ts, is there a tangible benefit? The pipeable module is used in almost all modules
It's not only about pipe being called but also about arguments passed to it. If everything is wrapped in pipe and pipe is not marked as pure then it, in turn, holds to everything and nothing can be tree-shaked.
The very similar situation was in Ramda - everything is curried, but by making similar changes some time ago I was able to make tree-shaking work for it 100%. If you import a single function from Ramda (since 0.25) then you will end up only with that single function and with what it depends on.
If you import a single function from Ramda (since 0.25) then you will end up only with that single function and with what it depends on.
If fp-ts wanted to achieve this, I think we'd have to remove pipeable because it's dynamic, and as soon as it's used for one import, it will bring everything else with it—all exports must be defined statically?
Oh, I've looked into what pipeable does under the hood - so yes, it would have to be removed to improve the tree-shaking story. It technically maybe could become a babel macro or something and just compile away, but that would rather only complicate things for it.
@Andarist I remember there was a period of hacks and Rollup-plugins for making Ramda including builds smaller:
https://github.com/polytypic/ramda-rollup-hack
https://github.com/idmitriev/rollup-plugin-ramda
There was, but such hacks are no longer needed for ramda. IMHO would be better to fix this idiomatically here rather than introduce custom hacks like this.
I have been working on making tree shaking work for the modules I use in matechs-effect, I can confirm that pipeable is a very problematic component.
Basically as noticed before having pipeable make the whole module non shakable, furthermore the sideEffect flag is only on a module level so whenever we include any function from a module we are including the full module.
I have changed the module structure to isolate each function in a single file basically one module per function and shaking is happly working.
Exmple at:
https://github.com/Matechs-Garage/matechs-effect/tree/features/tree-shaking/shaking/core/src/Array
One other thing that is important is how packages are published currently we have a dual /lib /es6 and this is not optimal, having a material-ui like publish mechanism where in each sub-module there is a package.json listing the es6 variant in it is better because the same code (even if required by a library) gets propertly indexed for the shaking algorithms, basically imports will have to look like "fp-ts/Array" where Array has itself a package.json so even if "fp-ts/Array" is required by a dependency we don't end up with the es5 version.
Slighlty unrelated (more an opinion) large typeclasses are also an enemy of shakability, for example option has a wide number of instances in it while for shakability is preferred to have variants with only for example Applicative or Monad like:
https://github.com/Matechs-Garage/matechs-effect/blob/features/tree-shaking/shaking/core/src/Option/instances.ts
&
https://github.com/Matechs-Garage/matechs-effect/blob/features/tree-shaking/shaking/core/src/Option/monad.ts
I am doing this for the modules listed at:
https://github.com/Matechs-Garage/matechs-effect/tree/features/tree-shaking/shaking/core/src
In case we decide to persue this for the core fp-ts I would be able to backport those here, the change will unfortunately be breaking because of the module structure)
--
update: rollup is slightly better compared to webpack, it will shake full modules (without pipeable)
What do you think about making these changes?
- Where a function is called on module instantiation, add a "pure" annotation comment
- Where an object property/getter is accessed on module instantiation, wrap it in an IIFE and add a "pure" annotation comment
@OliverJAsh I would give it a try, starting from handling pipeable calls first, which should give the greatest benefit.
So, for example, in Option.ts, this...
const {
alt,
ap,
apFirst,
apSecond,
chain,
chainFirst,
duplicate,
extend,
filter,
filterMap,
flatten,
foldMap,
map,
partition,
partitionMap,
reduce,
reduceRight,
compact,
separate,
fromEither
} = pipeable(option)
...will be replaced by
const pipeables = /*#__PURE__*/ pipeable(option)
const alt = /*#__PURE__*/ (() => pipeables.alt)()
const ap = /*#__PURE__*/ (() => pipeables.ap)()
const apFirst = /*#__PURE__*/ (() => pipeables.apFirst)()
const apSecond = /*#__PURE__*/ (() => pipeables.apSecond)()
const chain = /*#__PURE__*/ (() => pipeables.chain)()
const chainFirst = /*#__PURE__*/ (() => pipeables.chainFirst)()
const duplicate = /*#__PURE__*/ (() => pipeables.duplicate)()
const extend = /*#__PURE__*/ (() => pipeables.extend)()
const filter = /*#__PURE__*/ (() => pipeables.filter)()
const filterMap = /*#__PURE__*/ (() => pipeables.filterMap)()
const flatten = /*#__PURE__*/ (() => pipeables.flatten)()
const foldMap = /*#__PURE__*/ (() => pipeables.foldMap)()
const map = /*#__PURE__*/ (() => pipeables.map)()
const partition = /*#__PURE__*/ (() => pipeables.partition)()
const partitionMap = /*#__PURE__*/ (() => pipeables.partitionMap)()
const reduce = /*#__PURE__*/ (() => pipeables.reduce)()
const reduceRight = /*#__PURE__*/ (() => pipeables.reduceRight)()
const compact = /*#__PURE__*/ (() => pipeables.compact)()
const separate = /*#__PURE__*/ (() => pipeables.separate)()
const fromEither = /*#__PURE__*/ (() => pipeables.fromEither)()
right?
I noticed that we were bundling more code than we actually use
If I put up a branch with such a changes, would you be able to check whether they are actually working?
I can volunteer to checking out the changes
@gcanti
right?
👌
If I put up a branch with such a changes, would you be able to check whether they are actually working?
Absolutely!
Another alternative would be to kill pipeable given it will still add a lot of additional stuff to the bundle, we should also expose the data-last functions to allow other modules to not depend on the full instance, a prototype PR with Either is:
If I understand it correctly, pipeable could be left there for anyone not concerned with tree shaking to quickly implement pipeable instances, but not used internally
@Andarist @OliverJAsh I put up a 1087 branch (you can install it by running npm i gcanti/fp-ts#1087, the es6 folder is commited in).
Here's the repo I'm using to test different snippets https://github.com/gcanti/tree-shaking-test
Some examples:
Array snippet
rollup:
webpack
TaskEither snippet
rollup:
webpack
Changes so far:
/*@__PURE__*/ to pipeable calls /*@__PURE__*/ to monad transformers calls @gcanti Could you add the lib folder to that branch? My webpack config relies on it, because Node doesn't support ES Modules yet… so without the lib folder I can't build my app to test this!
@OliverJAsh done
That definitely fixes the issue I described in my original post.
When I tried it on the Unsplash app, the total size of fp-ts in our bundle decreased from 19 KB to 17 KB gzipped.
I can also confirm this improves things a little bit - the only "big" thing that could be done to improve the situation more is removing pipeable altogether, but this has severe API impact so not sure if you are willing to go down this road.
I would go for that, I even suggested to move to curried data-last typeclasses in 3.0.
I would go for that, I even suggested to move to curried data-last typeclasses in 3.0.
That should be carefully evaluate I am the opinion that it will have severe performance implications.
I can confirm removing pipeable improves significantly shakability (like also removing usage of transformers).
Another step would be to skin the typeclasses, instead of having 1 that bundles all toghether we might have many smaller to be used ad hoc in combinators, for examle instead of one implementing Applicative & Foldable have 2 implementing each separatedly.
That should be carefully evaluate I am the opinion that it will have severe performance implications.
I'm not sure about that. Most of the time we use curried data-last top-level functions which are produced by pipeable which makes extra call to original uncurried version. So this place should be improved.
The functions that take typeclass instance either call pipeable themselves to get generic curried functions (this should also be improved) or use the instance directly which ends in code inconsistency. And in such case I suppose we could sacrifice performance in favor of solving the whole bunch of problems by dropping pipeable. Still I think V8 should handle extra call in curried version pretty well.
@raveclassic I did a test a while ago making the base of matechs-effect curried and I was in fact paying a good 2x - probably it can still be worked around by not using the instance where perf count but we should do proper testing before. In respect of pipeable if you remove it you will not have the calls anyway regardless of the typeclass structure - anyway discussion for a different thread
discussion for a different thread
https://github.com/gcanti/fp-ts/issues/1216 if anyone wants to chime in
the only "big" thing that could be done to improve the situation more is removing pipeable altogether, but this has severe API impact so not sure if you are willing to go down this road.
AFAIK this can be done without breaking changes, it's just a lot of manual work
the only "big" thing that could be done to improve the situation more is removing pipeable altogether, but this has severe API impact so not sure if you are willing to go down this road.
AFAIK this can be done without breaking changes, it's just a lot of manual work
@Andarist @OliverJAsh I removed import { pipeable } from './pipeable' from all modules and updated the 1087 branch, could you please try it out?
Here's the results in my test repo (https://github.com/gcanti/tree-shaking-test)
Array snippet
rollup:
webpack:
TaskEither snippet
rollup:
webpack:
Task snippet
rollup:
webpack:
@gcanti Looks like it's missing the lib folder again?
@OliverJAsh opsss... sorry (commented out the wrong line in .gitignore) should be ok now
Thanks! Unfortunately I can't test this because it seems to have broken integration with https://github.com/devexperts/remote-data-ts

Is there a quick patch I can make to that library to fix this? Then I will be able to run some tests.
Looks like this isn't working for some reason: https://github.com/devexperts/remote-data-ts/blob/23a83624d0f72377c5467cf0a97d0c3ded5d68bf/src/remote-data.ts#L26-L30
@OliverJAsh weird, honestly I can't see how my refactoring could affect that library. Is there a way I can repro?
I added a RemoteData example in my test repo and it's compiling fine
It's working now. 😕 False alarm.
In our codebase:
It looks like our usage of fp-ts has changed since I last reported test results, and I can't remember which commit I tested it against last time. I'd be curious to test the changes without the removal of pipeable again, so I can see what difference that makes on its own (to make sure it is actually working for us). If you're able to publish those changes on a separate branch then I can compare all 3: 2.6.1, after https://github.com/gcanti/fp-ts/issues/1087#issuecomment-629806939, and then after https://github.com/gcanti/fp-ts/issues/1087#issuecomment-633814358 (the 1087 branch).
If you're able to publish those changes on a separate branch then I can compare all 3
@OliverJAsh branch 1087-PURE-only
Thanks. I'm seeing exactly the same size for both 1087-PURE-only and 1087. Not sure whether that means the additional changes in 1087 are not working, or whether it's just because we're unable to benefit from these changes because all exports are actually used.
it's just because we're unable to benefit from these changes because all exports are actually used
@OliverJAsh ^ this is the case I guess. In a fp-ts heavy based application the benefit should be less apparent, however in my test repo, which contains small scripts, the benefit is tangible.
Here's the stats
Array snippet
rollup:
webpack:
TaskEither snippet
rollup:
webpack:
Task snippet
rollup:
webpack:
Ok thanks for the help, I'm going to release v2.6.2
Awesome! Thanks for your work on this.
@OliverJAsh I'm working on v3 and there are some ideas that I would like to backport to v2 and see if they make any difference with respect to tree shaking
sequence (which is pipeable but currently is weirdly not exported)traversepipe to function.tseither into functorEither, applyEither, etc...)Note: top level traverse / sequence functions will allow to not import A.array which is huge
@gcanti
I'm working on v3
Yaaay! If you need any help, please let me know!
@raveclassic thanks, you can get a sneak pick on branch 3.0.0 (and 3.0.0-es6 which is installable from GitHub) but please keep in mind that I'm basically just experimenting, sometimes with big breaking refactorings, so I can't ensure any stability whatsoever in this early phase.
Closed by https://github.com/gcanti/fp-ts/pull/1232
@OliverJAsh just released v2.6.3 could you please try it out?
My TaskEither snippet
import * as _ from "fp-ts/es6/TaskEither";
import { pipe } from "fp-ts/es6/pipeable";
pipe(
_.right(1),
_.map(n => n + 1),
_.chain(n => _.right(n + 1)),
_.swap
);
went from 11K to 7K (rollup)
Stats:
rollup:
- [email protected]: 18K
- [email protected]: 11K
- [email protected]: 7K
webpack:
- [email protected]: 24K
- [email protected]: 9K
- [email protected]: 6K
Most benefits should come from replacing all occurrences of A.array.sequence (or A.array.traverse) with A.sequence (or A.traverse)
sequence/traverse references updated to use the new pipeable versions (🎉): 22.25 KB@OliverJAsh nice, thank you!
I know wither / wilt are less used in the wild than sequence / traverse but here's a PR for them https://github.com/gcanti/fp-ts/pull/1235
Most helpful comment
@OliverJAsh I'm working on v3 and there are some ideas that I would like to backport to v2 and see if they make any difference with respect to tree shaking
sequence(which is pipeable but currently is weirdly not exported)traversepipetofunction.tseitherintofunctorEither,applyEither, etc...)Note: top level
traverse/sequencefunctions will allow to not importA.arraywhich is huge