-
Notifications
You must be signed in to change notification settings - Fork 213
Argument namespaces #1955
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
Comments
If the referenced type is not in scope at the call site is it an error? Separately, I wonder whether it would be worth considering generalizing this to arbitrary receivers of some sort? That is allow instance receivers as well? |
I don't know if I understand this proposal well... You gave an example of Flutter color parameters:
However, if we have to explicitly declare the class/enum with the Considering the example you gave,
The only way I see to workaround this, considering the current stable Dart + this proposal, would be to extend the class and opt in to whatever we want in it, which IMO is cumbersome and annoying to maintain: class MyTextStyle extends TextStyle {
const MyTextStyle({
bool inherit = true,
Color? color from MyColors,
Color? backgroundColor from MyColors,
double? fontSize,
FontWeight? fontWeight,
FontStyle? fontStyle,
double? letterSpacing,
double? wordSpacing,
TextBaseline? textBaseline,
double? height,
TextLeadingDistribution? leadingDistribution,
Locale? locale,
Paint? foreground,
Paint? background,
List<Shadow>? shadows,
List<FontFeature>? fontFeatures,
TextDecoration? decoration,
Color? decorationColor from MyColors,
TextDecorationStyle? decorationStyle,
double? decorationThickness,
String? debugLabel,
String? fontFamily,
List<String>? fontFamilyFallback,
String? package,
TextOverflow? overflow,
}) : super(
inherit: inherit,
color: color,
backgroundColor: backgroundColor,
fontSize: fontSize,
fontWeight: fontWeight,
fontStyle: fontStyle,
letterSpacing: letterSpacing,
wordSpacing: wordSpacing,
textBaseline: textBaseline,
height: height,
leadingDistribution: leadingDistribution,
locale: locale,
foreground: foreground,
background: background,
shadows: shadows,
fontFeatures: fontFeatures,
decoration: decoration,
decorationColor: decorationColor,
decorationStyle: decorationStyle,
decorationThickness: decorationThickness,
debugLabel: debugLabel,
fontFamily: fontFamily,
fontFamilyFallback: fontFamilyFallback,
package: package,
overflow: overflow,
);
} |
@mateusfccp, I think you're last request may be a bit out of scope. The other proposals I've seen are to implicitly rename @munificent, I like this idea, I just have a few concerns:
|
@Levi-Lesches I didn't make any request. Instead, I commented on what was proposed based on what it was supposed to solve. The proposal's author specifically stated that the proposal should solve one problem, and I questioned how would it solve this specific problem considering some limitations. |
I don't think so. What matters is that the type is in scope where the API is defined. That's where the linkage is established. Then, from there, the parameter implicitly makes the names on that type in scope in the argument location.
That's correct and is a limitation of the proposal.
That's a really good point. A consequence of this proposal is that it means that the API accepting the shorthand must directly couple itself to the namespace where those identifiers are looked up.
In cases where you want to have your own set of short names for argument values, you can always simply define a bunch of top-level variables/constants in a library and import it wherever you want to use them.
Yes, this wouldn't be defining a restricted enumerated set of values, just a set of shorthands you can use to refer to some of them. |
Is this a proposal that would fit a lot of use cases ? It seems like it's mainly aimed at flutter. |
I think Flutter definitely exacerbates the problem since the Flutter API leans really heavily on named parameters and has a lot of parameters of enum or enum-like types. |
How could we define such a namespace for a parameter of type Alternativelly couldn't we have this notion of namespace in a similar way as import/export? For instance: namespace Colors for Color;
f() => Button('One', alignment: .top, color: .darken(.blue)); ( or directly in a library defining: export namescape Colors for Color; NB : I like the dot prefix because it avoids naming conflicts and it is explicit about the usage of a namespace. |
This might not be the right place for this, but for me function parameters came to mind, and I do like the idea of the prefixed dot:
feels similar to
However,
|
It's not clear what the Is it part of the function type? If so, does it affect subtyping? Probably not, so most likely it's not part of the function type. More likely it's static metadata carried along with the function type by the static inference and type propagation system, but completely absent at run-time. A kind of extra inferred knowledge about the parameter, but not something inherent to the parameter of the function type itself. Or it could be a feature which only works when calling an interface method (where we know the class declaring the interface) or static method (where we know the function declaration directly), not on a function value. |
I'm a huge fan of this proposal, hopefully this or something similar is integrated into the language at some point. |
I have a different take, why don't we allow nested ;statements? For example class Top {
class Inner {
static const int value = 1;
}
static const int value = 2;
}
void main(List<String> args) {
print(Top.value); // 2
print(Top.Inner.value); // 1
} And we allow a private class to be declared within any structure, for example void main(List<String> args) {
class MainInner {
static const int value = 1;
}
print(MainInner.value); // 1
} I think nested classes will make the code cleaner, more readable, and easier to maintain. And we can use shorter names without worrying about naming conflicts. |
This is a strawman proposal to address (some of) issue #357.
Here is an example of it in use. Say you have a couple of data types:
You also have this separate namespace-like class for working with colors:
Then you have a class that uses these:
To create an instance of this class today, you have to write:
You would like to be able to write:
This strawman enables that. But to turn it on, you need to change the Button and Colors classes like so:
The
from
clauses after the marked parameters are how those parameters opt in to special lookup rules for bare identifiers in arguments. When evaluating an argument expression:from
clause on the parameter, thenThe
from
clause lets an API author deliberately opt in to a set of identifiers that become valid arguments for that parameter. An API author can sort of say "here is the enumerated set of short names this parameter accepts".Pros
Compared to other proposals, this strawman more explicit and verbose. That explicitness provides a couple of benefits:
Identifiers can be looked up on another type
One of the common areas where users are frustrated by redundancy is color parameters in Flutter:
But, as you can see here,
red
isn't a property on the actual Color class, it's a constant on a separate Colors class. An explicitfrom
clause lets an API deliberately redirect to a separate type like that, like the example here shows.The namespace can be custom-tailored to the API
In fact, API authors can define their own custom namespace-like classes containing exactly the identifiers they want for a specific parameter. Any given parameter can have its own little purpose-built autocomplete namespace.
For example, you could do:
Here, the parameter's actual type is
int
, which isn't specific to any particular domain. There's no way we're going to add the ASCII table to the int class itself in order to have nicer looking charCode arguments.But since the author of a parameter chooses which type to look up argument shorthands on, this
fromCharCode()
constructor can point to a type specific to the API's domain.APIs are less fragile
The API author knows that changing the
from
clause can be a breaking change to uses of the API. If we rely on the parameter's type to determine which identifiers are allowed, then changing a parameter type is always a breaking change, even in ways that aren't breaking today.For example, say we decide that Button should also allow a string of CSS for its color. That means loosening the type of
color
to both Color or String:If the parameter's type determined what identifiers could be used, every existing callsite could have just broken. But by specifying the type that identifiers are looked up on explicitly, the API author can loosen the type while still preserving the lookup on Colors:
The parameter's type has changed, but every existing callsite relying on lookup on Colors continues to work. By not making the identifier lookup implicit based on the parameter's type, we give API authors more freedom to change parameter types without breaking users. They can evolve the parameter's type and its lookup namespace independently.
Cons
There are some negatives, though:
The syntax is strange and somewhat verbose. We've already packed quite a lot into the parameter list grammar, and this adds even more.
The benefit only applies to APIs that have opted in. If we roll out this language feature, it doesn't help any API users until the API maintainers have taken the time to update their parameter lists to take advantage of it.
The syntax only helps arguments. Other proposals based on context type can apply in any expression position where a context type may be available, like assignments, collection literals, etc.
We could fairly easily extend this strawman to support the right-hand side of binary operators (so
==
would work). Other syntactic locations are harder because it becomes less clear what API you should query to figure out what type to look up the names on.The text was updated successfully, but these errors were encountered: