Hi, I work on Dart and was one of the people working on this feature. Reposting my Reddit comment to provide a little context:<p>I'm bummed that it's canceled because of the lost time, but also relieved that we decided to cancel it. I feel it was the right decision.<p>We knew the macros feature was a big risky gamble when we took a shot at it. But looking at other languages, I saw that most started out with some simple metaprogramming feature (preprocessor macros in C/C++, declarative macros in Rust, etc.) and then later outgrew them and added more complex features (C++ template metaprogramming, procedural macros in Rust). I was hoping we could leapfrog that whole process and get to One Metaprogramming Feature to Rule Them All.<p>Alas, it is <i>really hard</i> to be able to introspect on the semantics of a program while it is still being modified in a coherent way without also seriously regressing compiler performance. It's probably not <i>impossible,</i> but it increasingly felt like the amount of work to get there was unbounded.<p>I'm sad we weren't able to pull it off but I'm glad that we gave it a shot. We learned a lot about the problem space and some of the hidden sharp edges.<p>I'm looking forward to working on a few smaller more targeted features to deal with the pain points we hoped to address with macros (data classes, serialization, stateful widget class verbosity, code generation UX, etc.).
I always feel better about the stewardship of a project when you see a thoughtfully written reason for saying no to a feature, especially when there’s already sunk cost. Props to the team.
This is good news. The dart language has been getting more complicated without corresponding quality of life improvements. A first class record object without messing around with macros would be a great start.
I think after reading through the blog post the reasons they have made a whole lot of sense and sounded like that of a mature engineering team to me.<p>There are a bunch of other interesting approaches here they can look at. Improving the code generation story more generally, shopping the augmentations feature (basically C#’s partial classes) and getting more serious about serialization all feel like sensible directions from here.<p>There is a really interesting community proposal at the moment on the serialization front that I think would solve a lot of the issues that got people so excited about macros in the first place here: <a href="https://github.com/schultek/codable/blob/main/docs/rfc.md">https://github.com/schultek/codable/blob/main/docs/rfc.md</a>
Macros give their own kind of power, and it's a tough call to give that up for runtime hot-reloading. Languages like Haxe have macros, but also have hot reloading capabilities that typically are supported in certain game frameworks. You probably don't want to mix them together, but it's also a good development process to have simpler compilation targets that enable more rapid R&D, and then save macros for larger/more comprehensive builds.<p><a href="https://haxe.org/manual/macro.html" rel="nofollow">https://haxe.org/manual/macro.html</a><p><a href="https://github.com/RblSb/KhaHotReload">https://github.com/RblSb/KhaHotReload</a>
> Runtime introspection (e.g., reflection) makes it difficult to perform the tree-shaking optimizations that allow us to generate smaller binaries.<p>Does anyone have any more information on How Dart actually does Tree Shaking? And what is "Tree Shakeable"? This issue is still open on Github <a href="https://github.com/Dart-lang/sdk/issues/33920">https://github.com/Dart-lang/sdk/issues/33920</a>.<p>I think this quote accurately sums things up<p>> In fact the only references I can find anywhere to this feature is on the Dart2JS page:<p>> Don’t worry about the size of your app’s included libraries. The dart2js tool performs tree shaking to omit unused classes, functions, methods, and so on. Just import the libraries you need, and let dart2js get rid of what you don’t need.<p>> This has led customers to wild assumptions around what is and what is not tree-shakeable, and without any clear guidance to patterns that allow or disallow tree-shaking. For example internally, many large applications chose to store configurable metadata in a hash-map:
Former Eng. Dir for Dart, co-founder of Flutter, here.<p>I'd like to believe this is a good thing for the Dart project, but only time will tell. My hot take here: <a href="https://shorebird.dev/blog/dart-macros/" rel="nofollow">https://shorebird.dev/blog/dart-macros/</a>
Maybe it's time to just recognize that lisp-style macros-as-language-syntax features just aren't worth the struggle and grief?<p>The big metaprogramming feature traditionally implemented in macros, type generation, is already provided in some form by all major languages already.<p>And an awful lot (and I mean an <i>awful</i> lot) of good work can be done at the string replacement level with cpp. And generating code upstream of the compiler entirely via e.g. python scripts or templating engines is a very reasonable alternative too. And at lower levels generating code programmatically via LLVM and GPU shaders is well-trodden and mature.<p>Basically, do "macros" really have a home as a first class language feature anymore?
Sounds like a good thing overall, my biggest annoyance when I was writing a flutter app was the codegen for annotations (which sure it's better iteratively, but the first one was taking minutes), but if you move these seconds that happen once in a while to seconds during "hot" reload, you're just losing. Honestly, I think they should try to come with a faster codegen, maybe write it in c++ or rust and fix these problems, because macros aren't a silver bullet. They introduce complexity, a new "thing to learn" and sometimes lead to Turing complete machines.
If the trade off for macros is speed, size and usability. I am glad they didn't merge macros just to tick a box and leave everyone saddled with a bad decision going forward.
FWIW, I enjoyed the hundreds of hours I spent with dart:mirrors to automate serialization, and the code-generation heavy approach always felt like kind of a bummer. But I feel like AI-assisted programming solves the majority of use cases this feature was meant for.
They should have just use C# for Flutter.<p>Without investing significant time, like they did with Dart, they would have a language with a much bigger ecosystem that is faster, already has compile time code generation and better support for data than Dart. It supports ahead-of-time compilation and hot-reload. The only feature missing in C# is compilation to JS, but with WASM is that really needed? Biggest downside of C# is probably that it's not invented at Google.
> <i>Semantic introspection, unfortunately, turned out to introduce large compile-time costs which made it difficult to keep stateful hot reload hot.</i><p>They must have done something wrong. Macros are expanded when you ahead-of-time compile your code, which doesn't take place in the run-time environment where you hot load, but in the build environment. It doesn't matter whether the macro are simple, or whether they can inspect lexical environments and look up type info and whatnot.<p>Compile-time <i>costs</i> should never factor into hot reload, because the stuff being loaded should already be compiled.<p>Maybe they aren't explaining it; there could be certain semantic problems preventing existing state from being re-used on what should be a hot reload.<p>Macros create certain issues in reloading. If you change a macro such that the expansion requires different run-time support which is incompatible with existing expansions, you have problems. One option may be to reload all the code which depends on those macros, so that everything cuts over to the new run-time support. If you need to support a mixture: hot-reloaded modules using the new versions of the macros, side by side with code made using the old versions, then the old version of the run-time support has to coexist with the old.<p>If the run-time support for the macros is something which manages state that needs to be preserved on reloads, then that can cause difficulties. The old and new macro expansions want to appear to be sharing the same state, not different silos.
What will the Dart team focus on instead? I wish the cross-compilation issue was taken to a higher priority, I mean Flutter already kinda of solved it.