Repository navigation
Description
Proposal: Enhancing Annotations in Dart with Postfix and Expression Syntax
Annotations, including macros, are powerful tools for ensuring systems are robust and for code generation. However, their current usage is limited by where they can be applied in the code. This proposal aims to remove that limitation while maintaining backward compatibility and promoting a consistent and deterministic coding style.
Overview of Changes
Annotations in Expressions
Consider a scenario where there is a performance bottleneck in your code, and you want to optimize a function, c, by inlining it or using an annotation to alter its behavior—such as disabling runtime checks. The function is widely used across the codebase, but the performance-related alteration should only apply at the bottleneck.
Currently, the closest approach to achieving this is:
@noRuntimeChecks final cResult = c();
a(b(cResult));This requires a verbose multi-line expression to specify the annotation. A more concise and readable solution would be to place the annotation directly within the function call:
a(b(@noRuntimeChecks c()));Even better, we could place the annotation after the function call to indicate that it's secondary to the implementation logic:
a(b(c() @noRuntimeChecks));This approach keeps the annotation close to the expression it modifies without interrupting the readability of the code. The annotation is still important, but it's less intrusive and aligns with the overall design of the code. This brings us to the other part of the proposed changes.
Postfix Annotations
One additional change proposed is the ability to define annotations that must be used in a postfix manner. This would enforce consistent and predictable code formatting, which improves readability, especially when dealing with development-time behaviors.
For example, suppose you want to log the result of a function call, validate its invariants, and ensure it's inlined. The current approach requires placing multiple annotations in front of the declaration:
@log @validateInvariants @inline final data = fetchData(id);With postfix annotations, this could be written as:
final data = fetchData(id) @log @validateInvariants @inline;This not only improves readability in a single line of code, but it becomes even more advantageous in larger code blocks where annotations might otherwise disrupt the flow. By placing annotations as postfixes, we ensure they are secondary details and don’t interfere with the core implementation logic.
One clear, but still simple, example for multiple lines:
Without postfix annotations
@log @validateInvariants @inline
final data1 = fetchData(id);
@log @validateInvariants @inline
final data2 = fetchData(id + 1);
@log @validateInvariants @inline
final data3 = fetchData(id + 2);With postfix annotations
final data1 = fetchData(id) @log @validateInvariants @inline;
final data2 = fetchData(id + 1) @log @validateInvariants @inline;
final data3 = fetchData(id + 2) @log @validateInvariants @inline;Implementation Considerations
To maintain backward compatibility and ensure predictable behavior, postfix annotations must be explicitly defined. This can be achieved through a marker type or by using a prefix annotation.
Example using an annotation:
@postfix const Null annotation = null;Alternatively, an annotation could implement a PostfixAnnotation marker type:
class Annotation implements PostfixAnnotation
{ const Annotation();
}These are just a few possible ways to implement this feature. The details of the implementation can vary, but the core idea is to have a clear distinction for postfix annotations.
Conclusion
Dart is already a powerful and customizable language, and adding support for postfix and expression annotations would significantly enhance its flexibility. This proposal aims to improve the readability of code by allowing annotations to be used in more natural and less disruptive ways. Additionally, I foresee this feature being widely adopted by Domain-Specific Languages (DSLs), as it would allow them to define more intuitive and natural syntax for augmenting expressions with additional behaviour.
Activity
TekExplorer commentedon Jan 28, 2025
Or... Perhaps... We can do what we already do and put all annotations before the element. As in, the line before.
TekExplorer commentedon Feb 8, 2025
@RohitSaily I don't really agree. Putting annotations after an expression would just get more confusing, and add ambiguity.
I think that the current simplicity makes more sense.
That said, we can definitely try to find improvements, such as expression macros, macros
TekExplorer commentedon Feb 9, 2025
Allowing annotations to come after an expression or statement makes it hard to tell that anything is happening.
Plus, it can mean that annotations can be "hidden" at the end of the expression.
Having the annotations ahead of the expression allows us to define the context of the following code before we read the code.
We can know that there's extra information to be had.
But if that information comes after, it can confuse you as your initial impression is now tainted - what you read may in fact be something entirely else thanks to the annotations.
I like what you're going for with the wrapper functions - but I think that the annotations should remain where they are.
Its basically like python decorators in that sense.
Expression macros would be way more powerful, as it may expand into who knows what.
TekExplorer commentedon Feb 9, 2025
Perhaps, but that is a theming/tooling change, and has nothing to do with the language itself.
That said, the formatter places annotations vertically, not inline, so perhaps you want to consider how that applies instead.
They could possibly be folded away like you might a code block or imports.
TekExplorer commentedon Feb 10, 2025
I still don't see the benefit. When would a postfix annotation ever be the sensible thing?
TekExplorer commentedon Feb 13, 2025
I see. I think I can understand that.
I actually don't agree here, since they would not be on the same line.
As you can see, you can see the declaration just fine.
I can see the point to having an annotation on the trailing end of a declaration like that, as you effectively put the marker exactly where the code will go.
Though perhaps it may be better to consider expression macros, in a sense:
It actually occurs to me that this can synergize with a different issue, (#3580) such that it's equivalent (in terms of actionable code) to:
Edit: actually heh, that issue is exactly the one mentioned above