Skip to content
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

Work with an anonymous object #259

Open
eernstg opened this issue Mar 8, 2019 · 0 comments
Open

Work with an anonymous object #259

eernstg opened this issue Mar 8, 2019 · 0 comments
Labels
request Requests to resolve a particular developer problem

Comments

@eernstg
Copy link
Member

eernstg commented Mar 8, 2019

This is a request for improved support in Dart for working with anonymous objects.

We already have the ability to work on anonymous objects in Dart, to some extent. In particular, we can use constructs like "Hello, world!".substring(0,5).toUpperCase() to perform a single operation on an object that has no name (here, it's the result of evaluating the string literal, but it could be any expression), and then perform an operation on the returned result (here: "Hello"), etc.

It gets more inconvenient if we wish to perform more than a single operation on the same anonymous object. Let's consider a few examples where that issue is addressed in different ways.

An Example

Consider this excerpt from the Flutter raw 'hello_world.dart':

// Version 1.

void beginFrame(Duration timeStamp) {
  // ...
  final ui.ParagraphBuilder paragraphBuilder = ui.ParagraphBuilder(
    ui.ParagraphStyle(textDirection: ui.TextDirection.ltr),
  )
    ..addText('Hello, world.');
  final ui.Paragraph paragraph = paragraphBuilder.build()
    ..layout(ui.ParagraphConstraints(width: logicalSize.width));
  canvas.drawParagraph(paragraph, ui.Offset(...));

  final ui.SceneBuilder sceneBuilder = ui.SceneBuilder()
    ..pushClipRect(physicalBounds)
    ..addPicture(ui.Offset.zero, picture)
    ..pop();
  ui.window.render(sceneBuilder.build());
}

This code has been written carefully in order to achieve a certain balance between conciseness and readability.

Note that we have several declarations (paragraphBuilder, paragraph, and sceneBuilder) that are only used once. This may be taken as a hint that the declarations could have been omitted, and the initializing expression for each of them could have been moved to that single location where the declared name is used.

So this is an example where an object is given a name, such that we can avoid to do just that. It is discussed below (version 3) why one might prefer to do it this way. But let's first consider the ways in which this example already works on anonymous objects.

Unfolding the Example

The code in version 1 already contains one construct which could be considered to be a response to this very issue, namely cascades: They allow us to perform several operations on an anonymous object. So we could unfold the example above to omit cascades:

// Version 2, unfolded.

void beginFrame(Duration timeStamp) {
  // ...
  final ui.ParagraphBuilder paragraphBuilder = ui.ParagraphBuilder(
    ui.ParagraphStyle(textDirection: ui.TextDirection.ltr),
  );
  paragraphBuilder.addText('Hello, world.');

  final ui.Paragraph paragraph = paragraphBuilder.build();
  paragraph.layout(ui.ParagraphConstraints(width: logicalSize.width));
  canvas.drawParagraph(paragraph, ui.Offset(...));

  final ui.SceneBuilder sceneBuilder = ui.SceneBuilder();
  sceneBuilder.pushClipRect(physicalBounds);
  sceneBuilder.addPicture(ui.Offset.zero, picture);
  sceneBuilder.pop();

  ui.window.render(sceneBuilder.build());
}

This transformation yields simpler declarations, but it introduces some statements. This means that the resulting code is "less declarative", and it may be harder to reason about because the reader will have to discover that there is no non-trivial control flow.

The other difference is of course that the names paragraphBuilder, paragraph, and sceneBuilder are now used several times. We now have more text and more redundancy, and that makes it harder for a developer who is reading the code to get the big picture.

This is the kind of motivation that we have for introducing cascades in the first place. So why don't we just go all the way?

Folding the Example

We could go in the opposite direction and eliminate all the single-use declarations. In other words, we tried to unfold the example and saw some problems, but we could also fold it:

// Version 3, folded.

void beginFrame(Duration timeStamp) {
  // ...
  canvas.drawParagraph(
      (ui.ParagraphBuilder(
        ui.ParagraphStyle(textDirection: ui.TextDirection.ltr),
      )..addText('Hello, world.'))
          .build()
            ..layout(ui.ParagraphConstraints(width: logicalSize.width)),
      ui.Offset(...));
  ui.window.render((ui.SceneBuilder()
        ..pushClipRect(physicalBounds)
        ..addPicture(ui.Offset.zero, picture)
        ..pop())
      .build());
}

The resulting code is more concise than version 1 and 2, and it stands out that we are performing a drawParagraph operation and a render operation.

However, the arguments to those two method calls are rather complex, and it's likely that a reader of this code would get an incorrect understanding of how many objects we are actually working on, which type each of them has, and what each of those operations is doing. Also, it won't help if the indentation is inconsistent.

Finally, a person who is writing this kind of code might easily put too many or too few parentheses here and there, so the writing process is basically subject to the same difficulties as the reading process, plus some steroids.

Situation

Dart seems to have an already decent amount of support for working on anonymous objects, but developers will easily get into some difficult trade-offs: We could use more complex expressions, or we could introduce more declarations, and there might be a sweet spot somewhere in the middle.

On top of that, it should be noted that we can only express a linear sequence of operations with a cascade (do this, then do that, just like a plain sequence of statements S1; S2; S3;). So if we do wish to use any non-trivial types of control flow (say, a loop) then we're lost: We will have to unfold, and use regular statements.

This issue is a request for more flexibility in this area.

@eernstg eernstg added the request Requests to resolve a particular developer problem label Mar 8, 2019
This was referenced Mar 8, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
request Requests to resolve a particular developer problem
Projects
None yet
Development

No branches or pull requests

1 participant