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

Rule proposal: Help with memoization #1864

Closed
fregante opened this issue Jul 20, 2022 · 8 comments
Closed

Rule proposal: Help with memoization #1864

fregante opened this issue Jul 20, 2022 · 8 comments

Comments

@fregante
Copy link
Collaborator

Description

Memoized functions rely on their arguments to work properly. Using global state means you also have to remember using the same global state in cacheKey.

Thoughts on how this can be avoided?

Fail

const api = mem(() => fetch(`/api/${location.hash.replace('#','')}`))
api() // while on the home
api() // while on the contacts page
const api = mem((name = location.hash.replace('#','')) => fetch(`/api/${name}`))
api()
api('contacts')
const api = mem((name) => fetch(`/api/${name ?? location.hash.replace('#','')}`))
api()
api('contacts')

Pass

const api = mem((name) => fetch(`/api/${name}`))
api(location.hash.replace('#',''))
api('contacts')

Additional Info

This is probably impossible to actually achieve in a linter though 🥲

Related:

@sindresorhus
Copy link
Owner

We could make a rule where you give it a list of method names where you want the function to be pure.

@fregante
Copy link
Collaborator Author

fregante commented Jul 21, 2022

I don’t think that’s possible though. Can I use fetch in a pure function? I think it’s difficult to tell apart the inputs (location.hash) from the output (the server response via fetch). Both are technically accessing “global state”

@sindresorhus
Copy link
Owner

@fregante Then you would have to use an ignore comment for fetch. At least it would force you to make an explicit choice about it.

@fregante
Copy link
Collaborator Author

Makes sense, but also:

1 first commit

// eslint-disable-next-line pure-memoization -- fetch is ok
const api = mem((name) => fetch(`/api/${name}`))

2 add default

// eslint-disable-next-line pure-memoization -- fetch is ok
const api = mem((name = document.title) => fetch(`/api/${name}`))

@sindresorhus
Copy link
Owner

That is not specific to this rule though. You can accidentally disable more than you intended with any rule. It's also not a realistic example.

@fregante
Copy link
Collaborator Author

fregante commented Feb 2, 2023

I agree with your last point regarding "making an explicit choice"

See these for a realistic example and the bugfix PR that such a rule would have prevented. Plenty of other such bugs in RG:

Another example where global/local scope should be blocked: serialized functions.

There's a slight difference though

  • help with memoization: block access to external state, but allow pure functions
  • help with serialization: block access to local scope, but allow browser globals

@fregante
Copy link
Collaborator Author

fregante commented May 21, 2023

Probably impossible or too difficult or too complex for unicorn:

I thought about potentially enabling this rule on specific files, maybe enforcing only /*#__PURE__*/ items, recursively, sounds like fools errand.

Maybe a rule that blocks any globals (except stuff like Math.*, Object.*) would be more useful. But even then, you can't guarantee that a local value doesn't depend on a global, like Date.now() or myElement.ownerDocument.location.href

@fregante fregante closed this as not planned Won't fix, can't repro, duplicate, stale May 21, 2023
@fregante
Copy link
Collaborator Author

I think the only possible helper for memoized functions would be a rule to block default parameters, to be enabled on-demand:

// eslint-enable-next-line unicorn/no-defaults
const api = mem((name = location.hash.replace('#','')) => fetch(`/api/${name}`))
//                    ^ Error: Default value not allowed
api()
api('contacts')

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

No branches or pull requests

2 participants