3 ms·
‘Most programmers can’t be trusted with macros’ is a sentiment I read a lot, but at one point in time people might have said the same about functions, and argua
by rauhl 8y ago
‘Most programmers can’t be trusted with macros’ is a sentiment I read a lot, but at one point in time people might have said the same about functions, and arguably they could say the same about pointers or recursion today.
I think that being able to think symbolically about one’s code is a vital skill for a programmer (and, more generally, that the capability of thinking abstractly is a vital skill for a learnéd human being). Thus, if someone lacks that skill, perhaps he should probably not be a programmer? That may sound revolutionary, or elitist, but I think most of us would agree that someone who can’t effectively reason about a depth-first tree traversal will have trouble as a professional software developer; mayn’t that also be the same of someone who can’t effectively reason about computational structure?
Your comment re. macros & fragmentation makes me wonder if you’ve ever used Lisp’s macros: I think that they actually mitigate against fragmentation, as e.g. CLOS started out as macros atop Lisp and ended up being standardised as part of it. Lisp’s packages help manage namespace collisions, and thus prevent fragmentation by making differences manageable.
And Lisp tooling typically handles macros very well indeed.
- beaconstudios 8y agothe unique problem with macros (at least in the C/C++ world) is that they can be used to invent new syntax. Now when you join the company, not only do you have to learn the code, but you also have to learn the language extensions at the same time. If there's a lot of them, you're effectively learning a whole new language with likely minimal intentional design, with all the potential problems that a new language comes with (unintentional/hard-to-understand side effects, performance issues in the output, poor domain fit, etc).
- shados 8y agoFor all the flack Java gets, it got something right on this topic imo. Take the lowest common denominator of language features (for a time), and have the solution to almost everything be code written with those very, very few constructs. Yeah, it ends up verbose as hell, but even though I haven't done Java since the early 2000s, I can look at our backend code (which is all in modern Java), and short of a few new things like annotations and lambdas, I can read it just fine. The problem space is very, VERY well understood. The patterns have been bikeshedded to death almost 20 years ago. It's super boring, there's not many interesting left, and it's awesome because of it. Of course, software engineers hate this in general because they get way too much enjoyment out of figuring out how to save 2 lines of code for their own good ;)
- pjmlp 8y agoThere are two things about Java syntax that are my sore points. Not having had the guts to make override a keyword, forcing the dumb @Override annotation. It is a matter of taste I know. But they could have made use of contextual keywords, thus keeping backwards compatibility. I really would like to have a typename kind of statement instead of making empty classes to simply typing with generic code.
- AkshatM 8y agoI think the point being made is about incurring unwanted technical debt, rather than about the skills of programmers. A macro enthusiast can leave behind a codebase that requires substantial expertise or siloed knowledge to work with. It's not that you can't trust people to make good decisions or invest in understanding what they're doing. It's that in real life people take shortcuts. Time constraints, deadline pressures, a brief skimming of a StackOverflow answer with no followup or further exploration - any of these can lead to code that does the job, is ignored until only one person is left who understands it, and needs to be parsed out and refactored. I think you can only trust programmers with macros (or functions, or pointers, or object-oriented programming) if you have good practices in place to handle technical debt (clean naming, clean separation of concerns, adequate testing, etc.). As a side note: the other big part about macros is YAGNI. Macros are fancy, but I've yet to come across a use case for it that can't be replaced with a simple function call. I'm also curious about how one would test a macro.
- pjmlp 8y ago> I'm also curious about how one would test a macro. The same way you test any other kind of function call?
- KingMob 8y ago> Macros are fancy, but I've yet to come across a use case for it that can't be replaced with a simple function call. For starters, macros are mandatory for out-of-order execution; anywhere you need arguments to be manipulated before evaluation must be done in a macro. E.g., a short-circuiting "or" function is impossible without being either a macro or built-in to the language. Another common use case is efficiency. Any substantial computation you do at compile/eval-time never has do be done at runtime. I've worked on a Clojure router that compiles its nested route structure into efficient string-matching/regex code before it ever gets turned into machine code. Other languages either have to re-parse the routes, or find a way to cache a more efficient representation as part of an extra build step.
- bunderbunder 8y agoI'm coming around to the same feelings about dependency injection that many others express about macros. In broad strokes, I really like that composition is increasingly being favored over inheritance. But I've also inherited some deeply Byzantine amalgamations of decorators and abstract factories that certainly do eliminate a lot of boilerplate. But they do it at the cost of sometimes making it very difficult to understand where certain behaviors are coming from. The worst is, most of them are ones that I inherited from my past self. The specialized knowledge it takes to maintain an application that's been written this way apparently fades quite quickly.
- btschaegg 8y ago> [...] but at one point in time people might have said the same about functions, [...] I really like your example, because it nicely highlights how much of a tooling problem this really is. To call a subroutine, you'll have to arrange your argument values on the stack, then do the call instruction, after which your target procedure pushes the return address to the stack... Except that almost no one does it this way anymore. I usually never think about all this stuff when I call `foo()`. Writing a macro in C/C++ is the equivalent of doing the whole legwork to call functions yourself. What I find really surprising, though, is that we haven't ever arrived at the point where we think that "most programmers can't be trusted with arbitrary mutability". It's everywhere, and since the C/C++ days, it got worse with many languages. I absolutely hate the house-of-cards code I regularly deal with in C# because people would have to write a couple of additional lines to encapsulate the internal state of their types properly. So instead, you get mutable public members or properties everywhere... * sigh *.