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 annotation(s) for exclusive params (@OnlyOneOf
, @AtMostOneOf
, @AllOrNoneOf
, ... ?)
#47712
Comments
I believe that the proposed annotation would be sufficient for I suppose that we could allow the annotation to be applied to the same parameter multiple times if it's mutually exclusive with more than one other parameter, something like:
If there are other constraints that package authors want to be able to express, would we introduce similar annotations (such as Should such an annotation be on the parameters, or would it be better to have them on the function:
That might require less repetition. It's also less error prone in some ways (you can't mistype the group name) and more error prone in others (you could forget to remove a parameter from a list while deleting it). Either way we probably want to have some consistency checks. For example, there needs to be more than one parameter in any given group / list. |
Would/could the lint also warn if it only finds one |
Yes. That's exactly the kind of consistency check I was trying to describe. |
Is it possible for linter to just look into the body of the constructor and partially evaluate |
Another way to restrict which pieces of data can go with other is to use separate sets of parameters.
could be
Which of course has drawbacks of requiring extra class as a wrapper, and actual names for Using annotations to mark parameters is interesting, although seems outside of the language, with name in strings instead of actual language constructs. |
Yeah. Support from the language would be better but (at least) overloading is a big ask. In the meantime we have a bunch of APIs like Defining a bunch of named constructors is definitely one thing we could do with the language currently but it'd be pretty disruptive for existing APIs and has I think some potential usability/discoverability issues.
This is a really interesting idea. I haven't thought about it too hard but a naive solution might be a little costly. (Caching could help here.) |
OnlyOneOf
annotation for exclusive params@OnlyOneOf
, @AtMostOneOf
, @AllOrNoneOf
, ... ?)
I agree, that's an interesting idea, but we only have that information for asserts that are in the initializer list and in a constructor marked as |
I don't understand this. I think @mraleph was not suggesting that we actually perform const evaluation, but that we "pretend" to evaluate certain asserts in functions (and constructor initializers) with a small set of supported operations. Something like: void f(int? a, int? b) {
assert(a == null || b == null); // At least one argument must be null.
assert(a != null || b != null); // At least one argument must be non-null.
} I think if we only supported: |
What about helper functions that called these protected functions? E.g. Container helper(Decoration? decoration) {
var color = decoration == null ? Colors.yellow : null;
return Container(
color: color,
decoration: decoration, // ERROR MSG: “Cannot provide both a color and decoration”
child: Text("Flutter"),
);
} This helper is legal; it only passes one non-null argument for |
First of all, glad to find this discussion/proposal here. Coming from dart-lang/linter#4805 (thanks @pq) I was reading and came to my mind that (and please correct me if I'm wrong) there could be parameters like What would that annotation be like? |
Maybe something like |
We do need to be careful about not making this more complex than it needs to be. |
Personally, I consider it a tenet of good API design that parameters should be as orthogonal as possible. If you have a single function that contains parameters that are mutually exclusive, then what you really have is multiple separate functions masquerading as the same one. Whenever possible, I think it's better to split those into separate functions. Overloading would be nice, but even in the absence of that, you can give the functions separate names. That also works for constructors since Dart has named constructors. Likewise, if you have a set of optional parameters that should all be passed together or not at all, that's a single parameter masquerading as multiple. Like @scheglov suggests, you're often better bundling that data together into a meaningful object which can be passed as a single parameter. With the new record syntax in Dart 3.0, you can do that without having to give it a name. A single nullable record parameter, instead of a series of separate nullable parameters means that you either have all the data or none of it, which is exactly what you're trying to represent. |
I like the idea of overloading it, Specially thinking about migrating from current widgets to new ones. Something like Container.decoration(
decoration: BoxDecoration(
gradient: LinearGradient(colors: [Colors.red, Colors.blue]),
borderRadius: BorderRadius.circular(10),
),
child: Text('Decorated Container'),
); Etc. |
What @munificent says. The one var not directly covered by multiple methods instead of mutually exclusive parameters, or record arguments instead of both-or-neither parameters, is the "only pass B if passing A" case, like "only pass It's probably still something that should be multiple methods, one without any timeout, and one with a required timeout duration and an optional (But then, everything can be solved using multiple members with different names, since everything here is something that languages with overloading just do using multiple members with the same name Sometimes just requiring an awful lot of members to cover the entire combinatorial space if there are several unrelated optional parameters. I guess that's why, fx, C# has both overloading and optional arguments.) |
TL;DR Use an annotation to document and (statically) enforce mutually exclusive named parameters
In Flutter there are APIs (such as
Container
construction) that enforce mutually exclusive named parameters (e.g., color or decoration but not both) at runtime. It would be better to provide this feedback statically.dart-lang/linter#3063 proposes a lint for the
Container
case, but there are similar patterns in at least:CupertinoSearchTextField
,Ink
,TextStyle
, andAnimatedContainer
. An annotation could support these patterns robustly for Flutter and ecosystem packages.Proposed Solution: an
@OnlyOneOf
annotation that allows API designers to identify groups of parameters that are exclusive.In
package:meta
:In
flutter/lib/src/widgets/container.dart
:In practice, a container created with a color and decoration would produce an analysis error like this:
To improve error messages further, we might consider adding an optional message to the annotation so that we could report something identical to the runtime exception (e.g., "The color argument is just a shorthand for 'decoration: new BoxDecoration(color: color)'.").
/fyi @bwilkerson @InMatrix @leafpetersen @munificent @goderbauer who were in on an earlier conversation about annotating exclusive params vs. a language solution like overloading.
/fyi also @natebosch @jakemac53 @lrhn @eernstg for more language team perspective
The text was updated successfully, but these errors were encountered: