Rendered at 14:36:04 GMT+0000 (Coordinated Universal Time) with Cloudflare Workers.
xg15 3 hours ago [-]
> Things have been changing drastically. There was once a point where it made sense to create suboptimal code if it meant that your team understood it better. Why? Because changes to the code provided more business value than the code itself being optimal, and if your team didn't understand it, no one could change things. But this is becoming less and less the case. If you can make code faster, better, even at the cost of readability and understandability, by you, but the AI can work with it perfectly fine, why wouldn't you?
I guess this will become the frontline of the upcoming civil war in programming-land...
Retr0id 7 hours ago [-]
The main problem with macros (and similar language features) is that they turn every codebase into its own DSL. When done tastefully it makes the code more readable, but there's still an overhead for newcomers to the codebase - they need to learn your DSL before they can be productive.
LLMs may make this overhead less visible to you, but surely it's still there? I'd rather more of my tokens went towards solving the actual task at hand, vs figuring out a custom syntax (and re-learning it on every fresh context window).
aeonik 2 hours ago [-]
I've never understood this perspective.
The patterns that macros express are still there in the codebase, usually via uncompressed repetition.
You would need to learn the pattern regardless if you used a macro or not, and a macro at least formally codifies the repetition.
Indeed, one can make bad abstractions, or messy extensions to the macro, but that's true of functions or classes just as much.
Not supporting macros seems to me as an arbitrary limitation put in place as a stop gap for a symptom with a different root cause. (I.e. removing expressive power to solve poor engineering or immature macro tooling).
It's definitely easier to mess things up with macros, but it's also easier to mess more up the higher up you go in any abstraction ladder.
Now that I'm writing this comment, the very fact that we have to distinguish a macro vs regular code feels like a design smell to me.
Functions should operate on data or code, and controlling when it applies, comp time, run time, or some JIT, should be a separate lifecycle configuration detail, or decoupled in a different manner.
sverhagen 4 hours ago [-]
I'm not stoked about macros, but the overhead for newcomers to the codebase, as an argument, I find weak. There's so many things that make understanding a new (to you) codebase hard, and I'm not hearing an argument that macros stand out, there.
AlotOfReading 2 hours ago [-]
An example DSL I wrote implements a prolog subset inside an imperative language (C++). Can you imagine the confusion a poor junior would have the first time they look at a file and it's all logic programming? It's hilarious, but definitely not a sledgehammer to be used lightly.
perrygeo 1 hours ago [-]
Like most comments here, I kinda throw up in my mouth thinking about all the weird DSLs that would come from unrestrained abstraction.
What about a slightly different angle: a custom language runtime? The language itself stays true to the original but you build your own custom compiler and dev tooling: an expanded stdlib, LSP, linting, formatting rules, build systems, test runners, package manger, host extension system, etc.
This is becoming somewhat of a reality in the Clojure world. https://clojure.cc/dialects/ lists ~30 languages which are recognized as "Clojure" but have radically different host environments. Of course Clojure has macros too so nothings stopping you from going overboard on the DSL weirdness.
I could see the following scenario: Your company picks Typescript. You evaluate Bun and Deno and others but nothing really works. You take the most promising one, build a little test suite to make sure it stays consistent with the language spec, fork it, and add the runtime features you need. Your dev team still writes Typescript but you have full control over the tooling and how that code works at runtime.
pie_flavor 11 hours ago [-]
Rust has been a practical experiment in macros for ten years now. I think it is safe to say the results are in: macros are great, and you just needed to have a better style of macros. Making and using something like `tokio::select!` in C-style macros is miserable, but `tokio::select!` is easy to use and understand in Rust.
xg15 2 hours ago [-]
Still learning Rust, but I found it unfortunate that basically the first command you're introduced to, println/print is a macro.
It's starting with the exception before you even had a chance to understand the rule.
kvemkon 39 minutes ago [-]
And one of the very first rules is:
> by default, variables are immutable
Well, since the name is a "variable", it must by default be mutable. Otherwise it is an exception being forced to become a standard case.
udderidder 4 hours ago [-]
[dead]
saint-evan 2 hours ago [-]
Hate the fact that when someone dreams up an article such as this [nasty word] where the central premise is that, '[SW engineering constraint] is no longer an issue anymore'... it's always because the solution is to turn off your brain and ask the LLM. It's almost similar to, 'because computers have gotten faster and memory's cheaper, we can write [nasty word] software' but this time it's, 'proper code architecture don't matter anyway because ain't gonna read it'.
Jesus! I am very certain, without evidence, that even LLMs have an upper bound for the shittiest, most warped code even they can understand and explain. How sad is it that we're going from 'this engineering constraint has been solved' to 'that's [LLM provider]'s problem'. There is a finite amount of context, a finite amount of signal that can be extracted from that context, and a finite amount of inferential reliability. Give the freaking chatbot a bizzare enough monstrosity and eventually the model's explanation becomes an increasingly plausible reconstruction rather than a reliable model of the program. This is the goddamned wall these damned idiots are about to run into... Taking on debt, pushing architecture in the OPPOSITE direction of human understanding AND banking on a neutral-at-best external entity to keep doing YOUR job for (essentially) free! And what's crazy is that I'm the absolute furthest thing from an Ai-hater AND I'm a 23yo JUNIOR PROGRAMMER but these sentiments are getting ridiculous asf on both ends of the AI preference spectrum.
tombert 11 hours ago [-]
At the risk of being a bit of a douchebag, it seems like nearly every tech opinion piece I read now boils down to "things used to be hard but they aren't anymore because LLMs can understand things faster than we can, so can ignore/change fundamentals!"
I didn't really find the argument convincing the first time and I don't really find it convincing now.
That said, with regards to macros, I actually do agree that the fear against them is broadly overblown. I have seen scary terrible horrifying macro soup in Clojure, but that has generally been outlier cases. Something like core.async abuses the hell out of macros, and despite that I think it very often increases readability and understanding of the language.
rgoulter 9 hours ago [-]
> it seems like nearly every tech opinion piece I read now boils down to "things used to be hard but they aren't anymore because LLMs can understand things faster than we can, so can ignore/change fundamentals!"
Sure, LLM coding agents being capable doesn't mean we can ignore fundamentals.
But LLM coding agents surely adjust the cost/benefit considerations for all kinds of programming efforts.
Arranging your code in such a way that it's so complicated you need an LLM to understand it is surely silly.
Using an LLM to write a macro because you weren't going to learn how to write macros and deal with all the subtle cases? I think that's arguable.
nvme0n1p1 10 hours ago [-]
And even if LLMs do our thinking for us, it's still not a reason to fork the programming language you're using, unless you expect me to believe an LLM can understand `foo |> bar` but it can't understand `bar(foo(x))`. If you're not looking at the code, it doesn't matter how pretty the code is anymore.
smitty1e 11 hours ago [-]
As long as GPS stays bullet proof, Navies are in good shape.
It's going to be a hard come-to-Beevis moment if the magic signal goes away and people have to remember celestial navigation.
Not sure you can even find a sextant on most vessels.
So too these AI services.
fragmede 10 hours ago [-]
GPS service is obviously going to go down whenever World War III goes hot, so the Navy is definitely training sailors on the alternatives.
QuesnayJr 11 hours ago [-]
The Navy was phasing out celestial navigation, but after some public outrage from retired naval officers they brought it back.
manyatoms 11 hours ago [-]
Does this mean using the star-tracking devices like what satellites have, or like a human paper/pen thing?
obviouslynotme 10 hours ago [-]
It should mean both. They need to learn how to do it by hand and then learn how to lean on a navigation system. If only GPS fails, then the celestial navigation program fallback runs without a hitch. If shit hits the fan, then they can break out the manual and remember how to do it by hand.
smitty1e 8 hours ago [-]
I mean, it is taught. In school. And, maybe, there is some residual skill out there in the fleet.
Doing a fix, like playing music well at all, takes copious practice.
999900000999 3 hours ago [-]
Please don’t.
Either make a full DSL, like GDScript, a general purpose language, or use one of the existing languages.
For me Unity using C# has been a very essential part of my career. I can learn tricks once and reuse them.
With Godot I understand why GDScript exists and we now have a fantastic community around it. C# Godot is still iffy.
I can’t imagine anything worse than joining a company and having to catch up on their specific frankien language with all types of macros and magic.
It sucks enough when they use non standard tooling or CI/CD systems. This company shall remain nameless, but once I had to work with a shitty CI/CD that took 4 or 5 hours for deployments.
flowerbreeze 6 hours ago [-]
I think being able to define additional rules in a language is a reasonable feature and sometimes would make life a whole lot easier, but I think when implemented through text macros rather than rewriting the underlying program graph along with accounting for all the constraints, that causes most issues.
Program graph rewriting is of course more complex, but can be better defined with regards to acceptable inputs and more defensive as opposed to letting the next step of compiler or interpreter handle it.
The other problem is that most IDEs don't offer an expand macro (or similar) option for the rewritten parts (or I haven't seen them doing that), so observing what the final code looks like requires additional steps. In some ways its similar to functions, but most languages have functions that have a well defined input/output structure whereas macros can do some unexpected things depending on the inputs.
classified 6 hours ago [-]
> most IDEs don't offer an expand macro option
Insert mandatory observation here that LISP was there decades ago. I don't know about SLIME in Emacs, but if you use SLIMV in Vim, you have a keyboard shortcut for MACROEXPAND already there.
d-us-vb 8 hours ago [-]
Not sure what this guy is on about. Languages with extensible syntax (through macros, through term rewriting, through intrusive function like decorators) have always been available, though notably less popular. Languages also can support extensible features through reflection: runtime inspection of structure of user-defined types.
- Any language with bicameral syntax (the lisps, etc)
- Rebol and Red
- Smalltalk, Self, etc.
People tend to not like these for most programming problems due to the eventual maintenance burden and the steep up front cost of becoming competent with them. But the title and thesis of this post is a bit of a head scratcher: we've always been able to change languages designed to be changed, and mainstream high level language no. 2, Lisp, was one such language. As always, whether or not a language supports macros or some general metaprogramming facility is a design tradeoff both for the complexity of the compiler and the implicit complexity of the codebase (knowing that the language doesn't prevent you from defining new operators with semantics that may go counter to the intuition employed in the base language).
Alien1Being 9 hours ago [-]
Basic argument:
I want it because LLMs make it easy to do without having to think.
pjmlp 8 hours ago [-]
Someone just asked for Common Lisp and Smalltalk.
brinepot 10 hours ago [-]
Moving from Java to Kotlin was a breath of fresh air; sometimes a language change genuinely unlocks productivity.
tinlid 11 hours ago [-]
My last project needed Rust for one module, but the rest was Go. Felt silly rewriting core logic just to stay unified.
KaiserPro 5 hours ago [-]
Thats a hard no from me Jimmy. There was a fashion for domain specific languages about a decade ago.
It was a nightmare.
Now hand wave about LLMs means we should just go right into machine code and not bother with intermediate. That statement is also as mad as the statement about just changing the language on the fly.
The need for langauge changes arguably has disappeared, as there isn't really a need to save keystrokes. the key thing is common understanding. If you let the LLM change the fundamentals, you're ending up with a logic puzzle on top of your problem.
spacedcowboy 4 hours ago [-]
I like Objective C. I worked at Apple for a couple of decades and I still think it hits the sweet-spot between expressiveness and complexity. Just a dozen or so more keywords over C and it became so much more. It certainly helped that it came with a really good class library. However, the writing is on the wall for ObjC, it has been for a decade, and I’m not a fan of Swift. So, being retired I thought I’d look at what I could do to create something in the ObjC vein, and maybe file off some warts along the way.
Enter ‘xc’ [1]. It’s cross-platform (Windows, Mac, Linux), and can compile for (amongst others) Windows, Mac, Linux, iOS, Android and WASM. I’m slowly building out an AppKit-like UI library [2] that supports all the platforms with the same View / delegate / datasource paradigm as AppKit, and an Interface-Builder like app that lets you associate the same code core with a variety of platforms layouts {desktop, tablet, phone,}
If you know ObjC, xc will be very familiar though there’s a lot less [] - the function/method calling style is C not ObjC. It has ARC and blocks (without the god-awful block syntax [7], I use 'block' as a type), threads, a Foundation like class library (no NS prefix). It doesn’t have header files, preferring to bind C libraries by reading the DWARF data in library for types/structs/classes/methods/functions. So a ‘#use <psql>’ statement will parse libpsql.so, read out all the types and let you use them. I adopted C-style structure ordering on the target platform to make this seamless.
As far as performance, it’s pretty much on-a-par with clang, on all 64-bit targets [3]. Sometimes a bit faster (notably method/function calls), sometimes a bit slower. That’s good because it gives me a target to still optimise. It’s basically float vectorisation that is the long tail, clang vectorises in places you might not initially expect, for good reason.
In some ways, this is going from a somewhat-still-popular language to a language used by precisely 1 person (me), but the advantage of owning the compiler is the ability to effect change in an afternoon, not 6 months. It’s also rather cool to have the same classes for the web-app that talks to your back-end server, and if you implement the ‘Codable’ protocol, to be able to serialise those classes seamlessly between WASM client and native server. Using xc let me write blewit.net [4] really easily, and one of the games within blewit (Monokracy [5], a web-client rewrite of Wolfpack Empire [6]) is in-process so I'd say its undergone at least some "in anger" testing, not just the 900 or so unit-tests.
So I agree that you should be able to change our languages, though perhaps my path and the author's differs somewhat :)
I spent years programming in Objective-C and loved it, it became one of my favorite languages. Swift has been a colossal letdown, as in "what were they thinking?!" but unsurprising if one pays attention to how macOS has (re|pro)gressed in the last decade.
sublinear 9 hours ago [-]
Not a single mention of Chesterton's Fence. Was this really written for HN, then?
I guess this will become the frontline of the upcoming civil war in programming-land...
LLMs may make this overhead less visible to you, but surely it's still there? I'd rather more of my tokens went towards solving the actual task at hand, vs figuring out a custom syntax (and re-learning it on every fresh context window).
The patterns that macros express are still there in the codebase, usually via uncompressed repetition.
You would need to learn the pattern regardless if you used a macro or not, and a macro at least formally codifies the repetition.
Indeed, one can make bad abstractions, or messy extensions to the macro, but that's true of functions or classes just as much.
Not supporting macros seems to me as an arbitrary limitation put in place as a stop gap for a symptom with a different root cause. (I.e. removing expressive power to solve poor engineering or immature macro tooling).
It's definitely easier to mess things up with macros, but it's also easier to mess more up the higher up you go in any abstraction ladder.
Now that I'm writing this comment, the very fact that we have to distinguish a macro vs regular code feels like a design smell to me.
Functions should operate on data or code, and controlling when it applies, comp time, run time, or some JIT, should be a separate lifecycle configuration detail, or decoupled in a different manner.
What about a slightly different angle: a custom language runtime? The language itself stays true to the original but you build your own custom compiler and dev tooling: an expanded stdlib, LSP, linting, formatting rules, build systems, test runners, package manger, host extension system, etc.
This is becoming somewhat of a reality in the Clojure world. https://clojure.cc/dialects/ lists ~30 languages which are recognized as "Clojure" but have radically different host environments. Of course Clojure has macros too so nothings stopping you from going overboard on the DSL weirdness.
I could see the following scenario: Your company picks Typescript. You evaluate Bun and Deno and others but nothing really works. You take the most promising one, build a little test suite to make sure it stays consistent with the language spec, fork it, and add the runtime features you need. Your dev team still writes Typescript but you have full control over the tooling and how that code works at runtime.
It's starting with the exception before you even had a chance to understand the rule.
> by default, variables are immutable
Well, since the name is a "variable", it must by default be mutable. Otherwise it is an exception being forced to become a standard case.
I didn't really find the argument convincing the first time and I don't really find it convincing now.
That said, with regards to macros, I actually do agree that the fear against them is broadly overblown. I have seen scary terrible horrifying macro soup in Clojure, but that has generally been outlier cases. Something like core.async abuses the hell out of macros, and despite that I think it very often increases readability and understanding of the language.
Sure, LLM coding agents being capable doesn't mean we can ignore fundamentals.
But LLM coding agents surely adjust the cost/benefit considerations for all kinds of programming efforts.
Arranging your code in such a way that it's so complicated you need an LLM to understand it is surely silly.
Using an LLM to write a macro because you weren't going to learn how to write macros and deal with all the subtle cases? I think that's arguable.
It's going to be a hard come-to-Beevis moment if the magic signal goes away and people have to remember celestial navigation.
Not sure you can even find a sextant on most vessels.
So too these AI services.
Doing a fix, like playing music well at all, takes copious practice.
Either make a full DSL, like GDScript, a general purpose language, or use one of the existing languages.
For me Unity using C# has been a very essential part of my career. I can learn tricks once and reuse them.
With Godot I understand why GDScript exists and we now have a fantastic community around it. C# Godot is still iffy.
I can’t imagine anything worse than joining a company and having to catch up on their specific frankien language with all types of macros and magic.
It sucks enough when they use non standard tooling or CI/CD systems. This company shall remain nameless, but once I had to work with a shitty CI/CD that took 4 or 5 hours for deployments.
Program graph rewriting is of course more complex, but can be better defined with regards to acceptable inputs and more defensive as opposed to letting the next step of compiler or interpreter handle it.
The other problem is that most IDEs don't offer an expand macro (or similar) option for the rewritten parts (or I haven't seen them doing that), so observing what the final code looks like requires additional steps. In some ways its similar to functions, but most languages have functions that have a well defined input/output structure whereas macros can do some unexpected things depending on the inputs.
Insert mandatory observation here that LISP was there decades ago. I don't know about SLIME in Emacs, but if you use SLIMV in Vim, you have a keyboard shortcut for MACROEXPAND already there.
- Any language with bicameral syntax (the lisps, etc)
- Rebol and Red
- Smalltalk, Self, etc.
People tend to not like these for most programming problems due to the eventual maintenance burden and the steep up front cost of becoming competent with them. But the title and thesis of this post is a bit of a head scratcher: we've always been able to change languages designed to be changed, and mainstream high level language no. 2, Lisp, was one such language. As always, whether or not a language supports macros or some general metaprogramming facility is a design tradeoff both for the complexity of the compiler and the implicit complexity of the codebase (knowing that the language doesn't prevent you from defining new operators with semantics that may go counter to the intuition employed in the base language).
I want it because LLMs make it easy to do without having to think.
It was a nightmare.
Now hand wave about LLMs means we should just go right into machine code and not bother with intermediate. That statement is also as mad as the statement about just changing the language on the fly.
The need for langauge changes arguably has disappeared, as there isn't really a need to save keystrokes. the key thing is common understanding. If you let the LLM change the fundamentals, you're ending up with a logic puzzle on top of your problem.
Enter ‘xc’ [1]. It’s cross-platform (Windows, Mac, Linux), and can compile for (amongst others) Windows, Mac, Linux, iOS, Android and WASM. I’m slowly building out an AppKit-like UI library [2] that supports all the platforms with the same View / delegate / datasource paradigm as AppKit, and an Interface-Builder like app that lets you associate the same code core with a variety of platforms layouts {desktop, tablet, phone,}
If you know ObjC, xc will be very familiar though there’s a lot less [] - the function/method calling style is C not ObjC. It has ARC and blocks (without the god-awful block syntax [7], I use 'block' as a type), threads, a Foundation like class library (no NS prefix). It doesn’t have header files, preferring to bind C libraries by reading the DWARF data in library for types/structs/classes/methods/functions. So a ‘#use <psql>’ statement will parse libpsql.so, read out all the types and let you use them. I adopted C-style structure ordering on the target platform to make this seamless.
As far as performance, it’s pretty much on-a-par with clang, on all 64-bit targets [3]. Sometimes a bit faster (notably method/function calls), sometimes a bit slower. That’s good because it gives me a target to still optimise. It’s basically float vectorisation that is the long tail, clang vectorises in places you might not initially expect, for good reason.
In some ways, this is going from a somewhat-still-popular language to a language used by precisely 1 person (me), but the advantage of owning the compiler is the ability to effect change in an afternoon, not 6 months. It’s also rather cool to have the same classes for the web-app that talks to your back-end server, and if you implement the ‘Codable’ protocol, to be able to serialise those classes seamlessly between WASM client and native server. Using xc let me write blewit.net [4] really easily, and one of the games within blewit (Monokracy [5], a web-client rewrite of Wolfpack Empire [6]) is in-process so I'd say its undergone at least some "in anger" testing, not just the 900 or so unit-tests.
So I agree that you should be able to change our languages, though perhaps my path and the author's differs somewhat :)
[1]: https://compile-xc.org
[2]: https://compile-xc.org/compiler/api/uxkit/guide-first-window...
[3]: https://compile-xc.org/compiler/performance/
[4]: https://blewit.net/sandbox/
[5]: https://0x0000ff.co.uk/img/blewit/monokracy.png
[6]: https://www.empire.cx
[7]: http://goshdarnblocksyntax.com (https didn't work for me)