-
Notifications
You must be signed in to change notification settings - Fork 205
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
Will modules facilitate partial classes? #1753
Comments
That sounds right to me too.
We're currently investigating static metaprogramming and macros. Macros would let you programmatically introduce new members right into a class declaration, so, if anything, that probably reduces the desire for partial classes. I think we'll want to see how that pans out before we would consider also adding partial classes to the language. |
What's the currently proposed way of doing that? Are the members being inserted directly into the AST/kernel/etc, or are they being generated as source code somewhere? The need for partial classes arise when you consider the features a lot of people want with metaprogramming (and is why I'm interested in them in the first place):
In my proposal at #1565, I use partial classes to accommodate for all of the above. // person.dart -- handwritten
part "person.g.dart";
@data
partial class Person {
final String name;
final int age;
String get greeting => "Hi, I'm $name and I'm $age years old";
} // person.g.dart
part of "person.dart";
partial class Person {
const Person({
this.name,
this.age,
});
@override
String toString() => "Person($name, $age)";
@override
bool operator ==(Object other) => other is Person && other.name == name && other.age == age;
Map toJson() => {
"name": name,
"age": age,
};
Person copyWith({String? name, int? age}) => Person(
name: name ?? this.name,
age: age ?? this.age,
);
} The two definitions would be combined when compiled, but the source code remains separate so it's clear what was generated and what was written by a human. At least, that's the system I went with; I couldn't think of a better one that hits all the points above. |
With the macros proposal, they are generated programmatically. You write some Dart code that uses and API to build up objects representing code in memory and then use that same API to attach those pieces of code as new declarations in classes, libraries, etc. It looks a lot like reflection, except that it's all happening at compile time.
The developer experience is really important for macros. It's not fully fleshed out yet, but our expectation is that, yes, editors and IDEs will have some way to go to definition into code generated by macros. Probably this means dumped the programmatically generated code out to disk somewhere.
That's a general goal of macros, yes, but there are valid use cases for replacing or at least wrapping the hand-authored code. Drawing the line between "too magical" and "too limited" is hard.
Since macros work programmatically, the generated code isn't really in "files" per se. The macro directly augments the declarations as first-class objects in memory. I agree it's important that users can tell which code is hand-authored and which code is produced by macros, but physically separating them in multiple files is just a means to that end. As long as the user experience is clear, I'm not too picky about how bytes are arranged on disk. |
Right, if macros can be done well without literally generating code in files, I'd be all for that too. It's just that after discussion in four to five issues, I can't find another way to let people see and understand the generated code without actually getting to see it interact with the rest of their project. For the most part, people seem to have no problem understanding that importing
I'm probably reading it wrong, but it sounds like you're thinking that macros can work in the backend without generating code, but will dump their definitions as code when asked? Shouldn't we stick with code to simplify that process and make sure devs can always verify visually that they're producing the correct behavior? Also because if code is generated when the IDE asks, that puts more work on the back of the IDEs that now have to manually generate the code and know when to safely delete the generated files. I get that keeping macros in the backend can be technically more efficient, but it might just be better for the dev experience if it were less magic and more like hand-written code. |
Possibly, yes. I'm basically just saying that we don't know how macro output will be visualized, just that it's a requirement that it can be.
Yeah, I think I am generally leaning towards something file-based too. I agree it makes a lot of stuff simpler (though it makes some other stuff harder). |
Hopefully partial classes can help with those, as in my example above |
I'd be a little worried by having classes declared across multiple libraries. Usually, so far, a class been declared and exported from a single library. If we allow different parts of a partial class to live in different libraries, then
Nothing insurmountable, I'm sure we can make that work. I'm just not sure the complexity is worth the effort. |
The motivation is code generation. With the constraint of generating code in a separate file, I tried to work out what that would look like when adding members to classes, and I experimented with extensions, mixins, subclasses, but nothing actually works. Extensions can't declare fields or constructors nor can they override members.
I tried to answer that in the top comment, but of course I wouldn't know the exact implementation details:
So:
|
The main advantage is that the generated library can have its own encapsulated imports which the main library doesn't need to worry about. |
I'd just let parts have that too. |
Heh, at that point, I think we're just arguing about nomenclature. A "part file" that has its own top level namespace and imports is what I'd call a "library". :) |
Yes, if the "part of" library exports and imports that "library", and they're able to share private names, and both contribute to the same partial class (so they'd have to be strongly connected). That's what I would call "one library". Using parts only you can't have two classes with the exact same name, and you can't do conditional imports on parts.Other than that, nothing prevents a package from being one big implementation library, spread into multiple parts, and one public API library exporting the public parts. And then we're back full-circle to defining compilation units/modules as sets-of-libraries or single libraries as sets-of-parts. Definitely nomenclature bike-shedding. We just need a list of what such two files can do together, and what otherwise unrelated libraries cannot, then we can decide which fits best. |
From #1749 (comment):
Would this discussion benefit "partial classes" (#252)? Previously, a few questions came up in discussion and in my head:
partial
declarations?Modules would answer all of those questions: the partial class may be split between different libraries, but the class as a whole would belong to the module. I saw that partial classes are not yet on the roadmap, maybe this can change that? Not to mention, both partial classes and modules are a huge step towards metaprogramming. Thoughts?
The text was updated successfully, but these errors were encountered: