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
core: Give JsonObject const constructor #1250
base: master
Are you sure you want to change the base?
Conversation
@davidmorgan is there something I need to do for this PR? |
@davidmorgan are there any problems implementing this PR? it's an important step towards having const constructors for all built values |
@@ -75,7 +75,7 @@ abstract class JsonObject { | |||
} | |||
} | |||
|
|||
JsonObject._(); | |||
const JsonObject._(); |
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.
I think we need const factories as well to make use of this private constructor?
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.
What do you mean ? You would want the
factory JsonObject(Object? value) {
line 78 to be const
?
It cannot be const
as it has a body with if
else
s
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.
I meant introducing multiple smaller factories, e.g.
const factory JsonObject.fromNum = NumJsonObject;
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.
Oh okay! I'm okay with this, I guess we should see what @davidmorgan thinks about that
Apologies for the slow response. Indeed, making constructors const does not help the factory, so I'm not sure this makes sense yet. built_value can't be const because of memoized fields, anyway, so that also needs more work. Maybe it would be helpful to back up a bit :) and ask what you want const built_value for, please? That would help motivate the changes :) I'm just now going on vacation for a week so my next response might also be slow, sorry in advance for that. |
in general we need const constructors for the ability to have a default value when providing an API to consumers. consider this example: myFunc({
BuiltList<String> scopes = BuiltList(["hello", "world"])
}) {} this is currently not possible, and the current alternative is to change the parameter type to nullable, like this: myFunc({
BuiltList<String>? scopes,
}) {
scopes??=BuiltList(["hello", "world"]);
} this is bad for the following reasons:
this of course extends to other scenarios, like myFunc({
JsonObject body = JsonObject.fromNum(42),
}); |
our specific usage is that I am part of a team that maintains the dart generator of openapi generator which has an option to use built_value as a serialization library. we are currently doing a rewrite that aims to improve the experience of api consumers by providing them with proper defaults. |
hi @davidmorgan did you have a chance to look at this ? |
Thanks. I'm not super excited about this direction for a few reasons, I will explain what I'm thinking :) First off, I'm not fully convinced by the defaults mechanism in the language itself. Now that we have null safety, this pattern works well: /// Do something.
///
/// By default do it one time, for a different number set [times].
void doSomething({int? times}) {
optional ??= 1;
// optional has now been promoted to non-nullable type `int` and is ready to go :) This has a couple of advantages over specifying the default in the signature:
Second, adding And third, this PR is only a partial fix, it doesn't make it so all of built_collection/built_value can be So, I'm not yet convinced about landing this. Happy to discuss further though :) Thanks. |
These might be advantages, but having a parameter as non-null and with a default value is a functional requirement of the API, especially when generating OAS schemas.
const doesn't disable all processing, it just disables processing on construction, if all processing is done lazily and cached (using
I can make a PR to fully transform all of built_value and built_collection if that's a problem :) |
Why does that happen, if you don't mind me asking--e.g. does the default end up in the schema?
Really the main valid use of Immutability in the sense of strict value immutability is also too strong: "immutable" data should be free to mutate as an optimization as long as that's hidden. You can use expandos for that, but there is a performance cost. Really what's wanted is that the values are "stable" in the sense of this language proposal if this language proposal moves forward--and I hope it does--then it should be the case that default values don't have to be This would be the right concept for built_value and would also work for your use case, I think. Unfortunately there's no ETA on the proposal, but with Dart 3 done I guess the language team has to work on something ;)
I'd be interested to see it, but I think it's unlikely to actually land as I'm not convinced about the tradeoffs and direction. Thanks :) |
consider the case of a nullable input with a default value that's not null, e.g.: void myFunc({int? myInt = 5}) {
} using built value, it's not currently possible to represent this in any way. |
While it's true that you can't easily replicate the behavior of
I'm not actually sure that's a good behaviour to want to replicate :) ... most often when I see this it's a bug, not a feature. e.g. just yesterday I fixed a case where a null value was being accidentally passed, wiping out the default; the correct behaviour was to have no default then set the default in the body, so null turns into default. For what it's worth, you can actually do exactly this with the same trick
But, I don't particularly like this trick. The other way that is not fully equivalent, but that I prefer, is the builder pattern. A builder can tell whether a field has been set or not, so it can choose to behave differently. This whole area was actually part of the inspiration for writing built_value in the first place :) |
the problem is that we are directly translating OAS spec directly into dart code, and in OAS it's normal and common to have a nullable field have a non-nullable value. but back to the main discussion, I think having const constructors are essential for a package that is all about immutability, and while dart's own classes aren't perfect (e.g. DateTime doesn't have const constructors), it would help greatly if built_value starts leaning towards this. |
It might be possible to make the code you generate do the right thing anyway by using the same trick as freezed or some other trick. I don't want to encourage using |
Fixes #1249
How would you want me to write tests for that?
Also, should I change and update
CHANGELOG.md
andpubspec.yaml
?