-
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
Nested cascades. #3043
Comments
I have to say that the kotlin scope functions really do work nicely for this. Your code above would look something like: new Cons().apply {
car = 42;
cdr = Cons().apply {
car = 7;
cdr = Nil();
}
} |
In C# we would do:
Very close to a JSON. If we had something close to this, I'd totally use a dart configuration file for my dartlang projects over a JSON/YAML. |
There is a delimiter, you just have to put it in a totally unintuitive place: new Cons()
..car = 42
..cdr = (Cons()
..car = 7
..cdr = Nil()); I see code internally like this surprisingly often even though it's not what I would consider readable. I would be delighted to have better support for nested cascades. But not like what you propose here. :) If I recall, @jacob314's original proposal was: new Cons().{
car = 42,
cdr = Cons().{
car = 7,
cdr = Nil()
}
} That looks pretty nice to me. I could see us maybe using parentheses instead of braces. Here's an example I found in the wild today: return AppendReq()
..proposedMessage = (AppendReq_ProposedMessage()
..id = (UUID()..string = event.uuid.uuid)
..data = event.data
..customMetadata = event.metadata
..metadata.addAll({
Metadata.Type: event.type,
Metadata.ContentType: event.contentType,
})); With your proposal, I think that would be: return AppendReq()
..proposedMessage = AppendReq_ProposedMessage()
...id = UUID()....string = event.uuid.uuid
...data = event.data
...customMetadata = event.metadata
...metadata.addAll({
Metadata.Type: event.type,
Metadata.ContentType: event.contentType,
}); With Jacob's syntax, it would be: return AppendReq().{
proposedMessage = AppendReq_ProposedMessage().{
id = UUID().{ string = event.uuid.uuid },
data = event.data,
customMetadata = event.metadata,
metadata.addAll({
Metadata.Type: event.type,
Metadata.ContentType: event.contentType,
})
}
}; |
Note that the new package:checks relies heavily on cascades and would also benefit from something here |
The example here actually can be done with parentheses, because it creates a new object. foo
..bar1
...baz = 42
...qux = 37
..bar2
...baz = 87
...qux = 117; This one cannot be helper with parentheses. |
@leafpetersen wrote:
Right. I'm tempted to mention that we do have a Dart proposal, anonymous methods, with a similar purpose and appearance: new Cons()..{
car = 42;
cdr = Cons()..{
car = 7;
cdr = Nil();
}
} These approaches differ in a few ways: The Kotlin functions accept a lambda as an argument. It may or may not be possible to inline both the scope function and the lambda such that the construct as a whole is just as fast as the statements in the body/bodies. Anonymous methods are designed such that this kind of inlining is definitely possible. Some of the Kotlin scope functions return the so-called context object (that's the receiver of the scope function invocation, like var theCons = Cons()..{ car = 1; cdr = Nil(); print('Hello!'); }; // We just want the side effects.
var theBlockResult = StringBuffer('').{ write('Hello, '; write('world!'); return toString(); } Note that it is in line with other parts of Dart to use Some of the Kotlin scope functions provide access to the context object using the keyword Anonymous methods by default use Of course, Kotlin scope functions are more general than Dart anonymous methods, because you can write your own scope functions to do whatever you want. However, I think the above remarks support the claim that the anonymous methods mechanism can easily do the things that the Kotlin community have expressed using a specific well-known set of scope functions. |
I dislike the "more dots" approach because it looks like the spread operator and IMO would hurt readability. |
Just came looking for a solution to this problem and having read all of the alternate syntaxes, the original proposed by @lrhn reads the cleanest. I don't agree that this will cause any confusion with the spread operator as the context is always different. a = [...listOfDependencies];
a = Pubspec()
..dependencies
...append(x)
...append(x)
.dependencies;
a = [...listOfDependencies, Pubspec()
..dependencies
...append(x)
...append(x)
.dependancies] That last line got away from me but now imagine if you had to add '{''s into the line. The core point is, that even though I used the 'new' cascade operator in an array, it was still clear what was going on. |
It's not possible to nest cascades, because the
..
always continues the outer cascade, and there is no delimiter. That is, you cannot do:and have the last two cascacdes apply to the second
Cons()
.So, what if we allow you to use more dots!
Every time you need to nest, you just use a cascade operator with one more dot. There is no upper limit (readability becomes an issue for other reasons before you reach six-seven dots, where it probably starts being hard to count).
We can either allow any larger number of dots, so you can do
a..b....c
and skip using three dots, or we can require always incrementing by one.The most pleasant is probably to allow any increment, so changing the outermost
..
to a single.
when removing the next-to-last member access, won't immediately make the rest of the expression invalid.We can always provide infos and quick-fixes to canonicalize the number of dots. Or perhaps even to increment the number in anticipation of adding an extra level at the outside.
The text was updated successfully, but these errors were encountered: