-
Notifications
You must be signed in to change notification settings - Fork 754
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
Consider returning an empty collection instead of an exception for MaxByAsync/MinByAsync #2166
Comments
The So when there are multiple items all of which end up being the 'maximum' (i.e., if you have a tie for first place) we return all of them, whereas the .NET runtime library just arbitrarily picks one and returns you that. Bearing in mind that these are, then, basically different operators (that just happen to have the same name), aligning the behaviours isn't really a goal for us, particularly since full alignment would mean removing functionality we have to today: If we were going to make a breaking change with the goal of fully aligning our But I don't think we want to remove this functionality. Existing Rx and Ix users relying on I don't think that's a price worth paying simply for making our Another option, I suppose, would be to retain the existing multiple min/max behaviour but to change the empty collection behaviour. But if the sole motivation here is to make Ix look a bit more like the runtime libraries, this seems only to get half way there. It just makes them slightly more like their namesakes, but still fundamentally different in an important respect. Is that a useful change? Remember the point of the Interactive Extensions was to add operators for So it's very much a non-goal of Ix to provide operators that look just like ones already in .NET. The only reason Ix defined I think if we were to change anything here, we'd want to ask how we should deal with the fact that since Rx and Ix defined the extended set of operators that went beyond standard LINQ, the .NET runtime has now added some new members, a few of which have naming incompatibilities with existing Rx/Ix operators. (E.g., they added There is an argument that we should add E.g., perhaps we should deprecate our existing But we'd need a plan for how this would avoid breaking existing code. Remember, one of the issues we have to deal with that a lot of libraries don't is that because Rx is very widely used, it's common for projects to have several components all with references to different versions of Rx. These all get unified to the highest version requested, and as a result of this, we really can't just change the behaviour of existing operators. It's not good enough for us to say "Well this is a major version increase, so we're allowed to have breaking changes." Consider the following setup:
The application is going to use v7.0 of The argument "Well it's a major version change, so you should expect this sort of thing" cuts no ice. But since these are widely used libraries, application authors might not even know there's an issue. Their dependency on either Ix or Rx might be hidden beneath many layers of indirect dependencies. So the bottom line is that we have to be a lot more careful. We can't simply interpret SemVer rules as a license to break anything whenever we like. In practice, we have to avoid breaking changes if at all possible, and in cases where they really are unavoidable, we have to introduce them painfully gradually, and in a way that a) gives people several years of warning that they are coming and b) provides ways to mitigate problems caused by the breaking changes. That's asking rather a lot more than you were probably thinking of doing, so if you don't particularly want to come up with a 5 year plan for how this could be done, that would be an understandable response to what I've written. But I wanted to explain (hopefully before you put any work into this) why if you submit a change that simply changes the basic behaviour of an operator that has been around for over a decade, we are extremely unlikely to approve that PR. |
First of all Ian, thanks so much for your detailed reply. I agree with what you're saying pretty much in entirety. Firstly, I believe that this library's method of returning a collection of the largest/smallest items is the correct way of going about the implementation, so I'm not suggesting changing that at all. We make use of Morelinq who subscribe to the same philosophy of returning a collection of items, although to deal with the conflicting .NET implementations they've recently chosen to rename theirs Maxima and Minima (indicating the plurals). I suppose, rewinding my comments about compatibility with the framework, what it comes down to is that I believe that returning and empty collection is a lot more useful than throwing an exception for these methods. Having no items in your IAsyncEnumerable is most likely not an "exceptional" circumstance, and an empty collection signifying there are no Largest or Smallest values seems logically correct. The way this library works currently means that you have to catch the exception, check your IAsyncEnumerable before calling the method, or do some workaround involving Based on your comments about the complexities of breaking changes in major versions, and the long term "goal" of aligning naming with the .NET Framework, my revised suggestion would be to create new methods Regarding putting the work into it, we'll probably be writing some if not all of these methods anyway in some way shape or form so as to avoid the workarounds I mentioned earlier, and I'm all for sharing this kind of infrastructure code which is why I wanted to contribute it back somewhere. |
Feature request
Behavior change on an existing operator.
The versions of this method in the framework only throw exceptions for ValueTypes, instead preferring to return null for reference types. In the spirit of this, it may be worth returning the empty List from these methods instead of throwing an error. I'll probably put together a branch with this on later this week for consideration and our own internal use.
The text was updated successfully, but these errors were encountered: