-
-
Notifications
You must be signed in to change notification settings - Fork 15.2k
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
Simplify composer #2145
Simplify composer #2145
Conversation
Nice! That definitely makes the code a lot cleaner and easier to understand |
My FP-fu is admittedly kinda weak. First issue I have here is I don't know if the changes actually keep the order of operations correct. Second issue is that even if the existing code isn't the absolute prettiest, it's there and it works. I'd need a pretty compelling reason to make changes to it at this point. Now, I do see a |
Might be a good idea to look through the revision history to see how it got to be this way. |
Yeah. As I've said a couple times, given Redux's current status and my own relative newness as a maintainer, my default attitude towards code change requests is "No unless it's really obvious it's a good thing". |
I think the main argument for the change is this: if something broke concerning the current implementation would you be able to a) understand what it’s doing, b ) explain it to someone else, c) be sure it wasn’t the source of the error. The update makes it easier to parse, and actually makes the composition function more easily testable. @gaearon history doesn't really show a great reason for it to exist that way. First commit has it using reduceRight with the head/rest chopping as well. Seems like the main changes that were made over time were things like guarding against non-functions being passed or short-circuits for stuff like single item arrays |
Did some digging through the history. Looks like the current iteration was made in #1050 . |
@markerikson that actually makes me feel like this change is even more important. The current implementation isn't a true compose. Given >1 arguments, the first function will be evaluated, before being composed with the rest. That seems broken |
I mean, the whole thing can be boiled down to
but I wanted to be as low impact as possible |
As I said, my own FP-fu is fairly weak. I'm going to ask you to simplify and clarify the issues involved :)
|
@@ -20,7 +20,5 @@ export default function compose(...funcs) { | |||
return funcs[0] | |||
} | |||
|
|||
const last = funcs[funcs.length - 1] | |||
const rest = funcs.slice(0, -1) | |||
return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@markerikson the last part last(...args)
in reduceRight
is evalutating, not composing. That is broken
A true compose is binding many functions together (think chaining) such that it reproduces one function that is callable at a later time. semi-bad example that should help demonstrate:
this is why getting order of operations right important for redux/middleware/etc |
Sorta following the concern here. Does the fact that this is normally used with store enhancers affect the usage/behavior in any way? |
Please add tests that fail with master. |
@gaearon there isn't really a test against it. It's just not really composing. It's using closures to make it ok to evaluate the last function as I didn't really expect this to be a big deal, I just noticed it while playing with some stuff and was just trying to help out. Thought it would be pretty obvious that it had some rust from changes over time by stuff like
I just figured that if I was fixing that, I might as well update the |
I’m having a hard time following this because I’m not really a smart programer. If it’s calling functions too early, we could test this by setting a flag inside and checking if the flag gets set during Thank you for your effort! I appreciate your help but my concerns are coming from a good intent, not from being stubborn or self-righteous about a broken implementation (see also #2146 (comment)). It’s just that if this is an observable change and people might rely on it, it should go into the next major. |
@gaearon #2033 looks like it's caused by a transpiler being confused by the current implementation as well. I assume a non-trivial portion of Redux users are using babel or something, which would mean that you've likely seen errors caused by this, but not clearly traceable back to this. You're clearly a smart and accomplished programmer, this isn't about ability. Ask my coworkers, I'm nothing special. We just use and love redux and I wanted to help out. I saw something I thought I could make better, and I'm sure someone will eventually see glaring problems with my patch and make it better as well. That's the benefit/nature of open source. It's an area that already has good tests, and it's a low impact change, if it causes side effects that are completely unknown (it shouldn't), then now we know a new case to test for and it's easy to revert. Admittedly, I'm a big fan of undocumented features/implementation being open game to be changed out from under people (i.e. don't play out of bounds), but I don't believe that's what's being done here |
Interesting, this might be the conversation about why this happened that you were thinking of #632 (comment) Looks like the |
Isn't code history spelunking fun? :) |
This PR and the original code are equivalent, minus the performance.
There's no observable eager evaluation happening, unless you monkeypatch |
@jridgewell timed over 10,000 iterations for each situation. It's doubly faster in the most common situation, and ranges between 3x faster and simply matching in other situations it's intentional that they are functionally equivalent. The new version should be more readable/predictable, doesn't do weird stuff like |
Interesting. Granted, the typical use case is "call once while setting up the store", but I suppose it's possible that someone out there might be reusing it for something more complicated. Thanks for the benchmarks. |
@markerikson 10000 iterations was just to prevent random issues with the computer from skewing the average. By common use case, I simply meant it being passed in between 1-10 items for composition. |
It depends on what you want to prioritize for, the current code is by no means optimized. http://justin.ridgewell.name/browser-benchmark/redux-compose/ |
I’m good with making this change. |
I like this change. But also, shouldn't the filter: compose(
isDev ? someEnhancer : null,
isDev ? anotherEnhancer : null
) it would miss out on that check. Also, recompose has the same function. I'd suggest also submitting this code change there. Ideally the implementations would be exactly the same to aid code minification. |
Filter change pr |
so is this good to go? |
Sorry, been focused on other tasks the last couple days. I see a 👍 from Dan, so let's do this. |
Thanks for the assistance, @gaearon and @jimbolla , and thank you @JoeCortopassi for the fixes! |
cleaned up the composer so that it doesn't rely on slicing and reversing the array of functions