Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Respect function argument count #18

Merged
merged 2 commits into from
Mar 21, 2018
Merged

Conversation

theKashey
Copy link
Contributor

A lot of libraries relay on arguments count - react-redux will deoptimize mapStateToProps if argument count is not 1, fast-memoize changes it's logic(and stops working as long arg count is 0), and so on.

The idea of this PR is simple - just pass down the original function length. As long it is readonly - do it via descriptor.

@codecov-io
Copy link

codecov-io commented Feb 23, 2018

Codecov Report

Merging #18 into master will not change coverage.
The diff coverage is 100%.

@@          Coverage Diff          @@
##           master    #18   +/-   ##
=====================================
  Coverage     100%   100%           
=====================================
  Files           1      1           
  Lines          16     19    +3     
=====================================
+ Hits           16     19    +3

src/index.js Outdated
@@ -33,6 +33,12 @@ export default function <ResultFn: (...Array<any>) => mixed>(resultFn: ResultFn,
return lastResult;
};

Object.defineProperty(result, 'length', {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you just do result.length = resultFn.length?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, function.length is readonly property, reflecting the numbers of arguments. The only way to override it - define a descriptor.
Actually, the best way here is to pass through all the descriptors from the original function, including name, and any static prop, that may exist, but I decide to keep your code compact.

it('should maintain function arguments count', () => {
expect(memoizeOne(a => a).length).to.equal(1);
expect(memoizeOne((a, b) => a + b).length).to.equal(2);
expect(memoizeOne((...rest) => rest).length).to.equal(0);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this equal 0?
and should the next one equal 1?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, the final form of this function operates on arguments, not leaving anything in the function signature.
That's the problem.

@theKashey
Copy link
Contributor Author

function1 -> memoized_function1
() => {} -> memoized_one (discussable, may be should be '')

Follow up -> may be .toString should return the original toString, as long lots of libraries detect arrow or bound functions using string representation of a function.

src/index.js Outdated
@@ -3,6 +3,13 @@ type EqualityFn = (a: mixed, b: mixed) => boolean;

const simpleIsEqual: EqualityFn = (a: mixed, b: mixed): boolean => a === b;

const defineProperty = (target, property, value) =>
Copy link
Owner

@alexreardon alexreardon Feb 26, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you please add some flow types to this function?

(target: Object, property: string, value: mixed): void

@@ -33,6 +40,10 @@ export default function <ResultFn: (...Array<any>) => mixed>(resultFn: ResultFn,
return lastResult;
};

defineProperty(result, 'length', resultFn.length);

defineProperty(result, 'name', `memoized_${resultFn.name || 'one'}`);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am a little worried that this might slow things done a bit. I am not sure if the effort is worth the sugar. Would you mind taking a look at the impact?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is possible to make this getter. Then it shall not have any impact.
Anyway - it is easy to test.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps we could make them both getters! As you said - worth testing!

Thanks @theKashey!

@@ -33,6 +40,10 @@ export default function <ResultFn: (...Array<any>) => mixed>(resultFn: ResultFn,
return lastResult;
};

defineProperty(result, 'length', resultFn.length);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but we should totally ship the length property!

@alexreardon
Copy link
Owner

Any update on this one @theKashey ?

@theKashey
Copy link
Contributor Author

Yep, sorry - forgot to update.
As long these operations are made on spinup time it does not affect anything at runtime. Also, in your current cases, nothing is going to access .length or .toString at runtime, and they also will not affect performance at all.

I've tried to stress test 2 versions of memoize-one, and the delta was inside "delta", ie 1-2%.

@alexreardon alexreardon merged commit 8aa8338 into alexreardon:master Mar 21, 2018
@alexreardon
Copy link
Owner

Thanks @theKashey !!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants