This is why the D programming language doesn't have macros, neither text nor AST. It's been a controversial decision, and I've had trouble convincing people of the reasons why. Others explain it as "Walter doesn't like macros."
The reason is that macros enable one to invent one's own language withing the language, and that language is incomprehensible to others. It happens with every language that supports macros and macro-like constructs. (It happens with C++ "expression templates" and operator overloading.)
People swear up and down that they'd never use macros that way, they'd use them responsibly, and the incomprehensible mess they create is indeed responsible.
For a hilarious example, an engineer I know recounted a story at Big Corp where there was an aged piece of software written in assembler. The programmer for it had long since moved on. The code had some problems and needed updating. Unfortunately, the programmer had invented his own language using the macro system, and nobody wanted to touch it.
My engineer friend said he could do it, and had the code fixed in a couple hours. Amazed, the manager asked how he'd figured out those macros. He replied "I didn't. I assembled the code, ran it through a disassembler, converted that back to source code, then fixed it."
CTFE returning strings to be mixed in is 90% of the way to a proper macro system. (It just needs quasiquoting, really.) The only thing keeping it from taking off is how ugly it is to use, keeping it relegated to hidden tooling code. And of course there's UDAs, mixin templates having full access to the lexical space they're mixed into, `q{}`... really, D has all the pieces of a fine macro system, you're just refusing to take the final step and make them consistent and easy to use.
It kind of reminds me of Path of Exile and their refusal to implement proper player shops. It doesn't mean there's no item trading, it just means there's only bad and annoying item trading. I guess I don't believe in gimping tools, especially when you're already willing to take them most of the way.
Similarly, operator overloading is deliberately constructed in such a way as to allow new arithmetic types to be smoothly designed, but to make it clumsy to do things like C++ IOstreams.
- people who want something more dead-simple / no-magic go for Go
- people who want the full combo of "max performance" + "performance-wise-free abstraction" + "extra safety" go for Rust
And conservatives stick to C++. Hence D didn't get very popular. I think it's a programmer psychology issue: people just don't like "middle ground languages" they want to clearly be in one extreme and accept its tradeoffs.
(There's Swift as a counter-example currently growing outside of iOS-dev towards ML, but I guess it's more like it being "like Go but with operator overloading and some macro-like infra features that ML people want & need"...)
Turns out everyone uses reflection instead to get anything done.
Not sure how much of a success that is.
In any case, this whole debate, starting with the submitted blog and on down from there, is a lot of nonsense that completely ignores the fact that humanity has produced thousands and thousands of programming languages, only a handful of which were ever popular at any given moment. The numbers are stacked against any language, regardless of its peculiarities and merits.
What is popular in this area is a combination of luck, promotion by some big company, or bundling with an operating system or application platform.
I just found this in the man page (for a fairly dated version of gcc):
-fdump-translation-unit (C++ only)
-fdump-translation-unit-options (C++ only)
Dump a representation of the tree structure for the entire
translation unit to a file. The file name is made by appending .tu
to the source file name, and the file is created in the same
directory as the output file. If the -options form is used,
options controls the details of the dump as described for the
-fdump-tree options.
If this were Lisp, we wouldn't be scared of what "representation of the tree structure" will look like.It doesn't say anything about whether templates are expanded/instantiated.
But didn't Java go through the whole Factory/Design Pattern/Deep Inheritance nesting that created a large amount of complexity on big projects? Was that really better than creating a DSL that a newcomer to the team had to learn?
Maybe the reason is that Java was just more popular and more familiar to a majority of programmers. Worse is better often because worse is more popular for historical reasons.
The magic was always that it was always an AST all the way down, and you can create and experiment with your own personal semantics and vocabulary, so long as the language is expressive enough to define them in the domain specific way you are doing development. For example a language that is aware of what libraries and databases you're tying together and abstracts the way they interconnect, can compose multiple libraries together without the kludgy plumbing, or autogenerates boilerplate along the way that's required by your build and tests. As long as you generate readable clean Java or whatever, your coworkers won't care how you got there
Both allow the massive freedom of something like a Lisp but you get to leverage the massive existing codebases in both environments.
Reason I ask is because I'm just starting to learn Kotlin (or will when the book turns up from Amazon) and I'm curious.
I decided to go the book route because I've found that when learning a new language the book route works better for me since it gives a structured walkthrough of the language rather than a piecemeal one and it's more time efficient.
YMMV but for me you still can't beat a good programming language book.
Best of luck to you! I've never been more productive than I am with Kotlin.
We already have many of those for Common Lisp.
Ultimately, the problem boils down to the languages giving too much freedom. More conventional languages like Java can scale to a massive degree because they standardize so many things, which means that you can now share components which amplify your work. Lisp et al are cool toys and small team tools, but you're not going to see any large scale db libraries and parsers and concurrency libraries and web servers and cachers and HA systems that can interoperate with each other without a ton of shoehorning.
When every foreign chunk of code requires a ton of ramp up time just to figure out what it's doing and how you could even integrate it, or requires changing your IDE and workflow in ways incompatible with what you have already, you lose more than you gain.
Worse is better. Worse gives you interoperable, reusable, amplifiable components, in a common framework everyone understands.