PR от Славы Егорова по более простой реализации статического метапрограммирования в Dart был прият и теперь красуется в репозитории спецификации языка 🥳
🥲 Почему появилась эта спецификация?
Многие же из вас знают печальную историю с тем, как команда Dart жидко обделались с макросами и насколько кастрирован dart:mirrors?😢 Рефлексия - довольно мощная, но сложная и медленная штука, которая плохо совместима с AOT-компиляцией. Из-за этой особенности было принято решение полностью отказаться от возможности хоть как-то использовать эту встроенную библиотеку в продакшене.
Казалось бы - ну и что? Жили же с кодогенерацией без макросов и рефлексии!!! Но она требует отдельной инфраструктуры, усложняет разработку и не всегда удобна. Поэтому потребность в метапрограммировании до сих пор остается 😄
🧩 Новая спецификация предлагает:
- 👉 Введение аннотации
@konst, указывающей компилятору выполнить вычисления на этапе компиляции. - 👉 Возможность интроспекции структуры программы прямо во время компиляции.
@konst final String data = computeData(); // вычислится при компиляции
Говоря более простым языком - разработчики смогут использовать часть Dart-кода, который ранее был невозможен в константных выражениях (циклы, условные операторы, списки и таблицы/карты).
⚡️ Что изменится?
Появятся Const-выражения (привет constexpr из С++):
@konst final attributes = buildAttributes();
Дженерики смогут компилироваться в конкретные реализации:
void foo<@konst T>(T value) {}
foo(42); // на этапе компиляции создаст конкретную реализацию foo<int>
Циклы будут раскрываться при компиляции:
for (@konst final v in [1, 2, 3]) {
print(v); // на этапе компиляции развернётся в print(1); print(2); print(3);
}
🎯 Практическое применение
Если фича релизнится, то такие вещи, как JSON-сериализация, генерация метода hashCode и operator==, или автоматическая маршрутизация запросов можно будет реализовать прямо внутри Dart без дополнительного кодогена, что положительно скажется на точке G вашего проекта, т.к. значительно сократится количество файлов "*.g.dart"
mixin DataClass<@konst T> {
@override
operator ==(Object? other) {
if (other is! T) {
return false;
}
final typeInfo = TypeInfo.of<T>();
for (@konst final field in typeInfo.fields) {
final value1 = field.getFrom(this as T);
final value2 = field.getFrom(other);
if (field.type.isSubtypeOf<List>()) {
if ((value1 as List).length != (value2 as List).length) {
return false;
}
for (var i = 0; i < value1.length; i++) {
if (value1[i] != value2[i]) {
return false;
}
}
} else if (value1 != value2) {
return false;
}
}
return true;
}
@override
int get hashCode {
final typeInfo = TypeInfo.of<T>();
var hash = HashHelpers._seed;
for (@konst final field in typeInfo.fields) {
hash = HashHelpers.combine(hash, field.getFrom(this as T).hashCode);
}
return HashHelpers.finish(hash);
}
Map<String, Object?> toJson() => toJsonImpl<T>(this as T);
}
// Example
class A with DataClass<A> {
final int a;
final String b;
// ...
}
Уже видите профит от этой Static Enough Metaprogramming?
- 👍 – О, да, детка!!!
- 👌 – Сомнительно, но Окей
- 👎 – Отстой! (Не превращайте Dart в C++)




Дискуссия