Bad programming ideas that only become apparent after years
twitter.com
twitter.com
For example, in lisp, macros are used ubiquitously, and I think if anyone was going to criticize macros as a language feature it would be based on what they've seen in lisp (or C, but let's ignore that for the moment).
Meanwhile, in python, there are extensive metaprogramming facilities that could easily be used to make code very hard to understand (Even if you never use eval, you have decorators, metaclasses and descriptors etc).
I think the difference is cultural. Python has a norm of resorting to metaprogramming as a last resort, and largely in libraries and frameworks, not in application code. Certainly there's no compiler forcing people not to use all of the flexibility available in python at every turn. Even compared to a similar language like Ruby, python metaprogramming use is generally pretty restrained.
(My understanding of the implied context is that Walter Bright, the creator of D, is likely looking at macro usage in lisp, and inferring that macros are a mistake in a language like Rust. I know Walter is on here pretty often so if I'm guessing wrong here feel free to correct me)
I suspect there is also a generational divide.
Lisp is such an old language that I often wondered if it has so many macros because they didn't have the equivalent of "defun-inline". ie. That far too many usages of macros in the Lisps were for performance rather than for abstraction.
In modern languages, though, macros are really the only way to get at something like the AST of a language and act like the original developers of the language. I personally approve of this--the Go "Me, but not for thee" attitude of the language developers pisses me off.
However, given such power, usage of a macro should set off your "I'm using a macro. This language ecosystem is deficient somehow". And that should give you pause and you should ask "Am I really adding something to this language or am I just carrying over my habits from other languages?"
Finally, using a macro merely for "abstraction" should set off gigantic klaxon warning bells. A macro has to pass a really high bar if you could actually do the task with just functions and a little extra bit of keyboard typing.
I’m not sure it makes it a ‘good’ idea, but I recently needed to overload [] on an embedded system. The chip we used in a design didn’t have enough pins for both SPI and external memory. One of the memory address pins was used for both. So we wired up the memory skipping over the pin. This led to odd behavior that didn’t make sense until the original hardware engineer remembered he’d had to build it this way as a price compromise (the chip with enough pins would have probably been double the price). So I had to make sure that an adress translation happens on every access to DRAM, and overloading array access was a very obvious clean way to do this for any code that already did array access.
There were really no clean ways to deal with this hardware compromise. Avoiding [] to Leave some bread crumbs around is maybe an ok solution. But there are bigger breadcrumbs around, like the malloc() implementation that is needed for this hardware.
http://literatejava.com/exceptions/checked-exceptions-javas-...
I would generalize to the following: Everything that increases the time required to read the code is bad if it does not reduce the amount of code by a larger fraction.
So, refactoring code into a simple function that occurs once or twice is Ok, as long as that function can be found quickly in the same context (file, class, etc.). If the context is elsewhere, though, it is bad.
Similar for anything implicit (operator overloading, conversions, fancy type classes): If you need to switch context or even know about a completely different language feature or library to understand this code, that additional effort better saves a lot of code here.
They are both abstractions that substitute an explicit operation with just a name; and thus both require that you expand your vocabulary when holding the program context in your head. Abstract at the wrong level, and you increase difficulty; abstract at the correct one, and you lower it.
Where I do think the main problem with macros is, is in debugging - because I know no macro system that maintains metadata on the written code to help explain the higher (macro) conceptual level once a stacktrace / condition / segfault / error arises.
That said, with the proliferation of distributed systems that are commonly executing where you can't connect a debugger, I've found many aren't even aware of what they are missing. Jrebel, I recall, was very popular in some circles. Today, I'm not even sure most of my colleagues have ever used a debugger in a professional context.
Macros can be great if used to overcome a syntactical limitation, in which case they can make the code more readable and compact. If it introduces barriers to reading the code it's not a good macro, and maybe unnecessary.
1. Garbage collection + Forced reference counting. Why penalise all programs?
2. Coroutines and futures. Just use actors, please.
3. Visual Rapid development tools. Because the moment you need to do something interesting, the tools limit you.
But for most problems, these don’t impose a meaningful penalty and do remove a huge cognitive burden. Why penalize all programmers?
We stick with this despite that stack allocation is vastly faster than heap.
"The trouble was the author had used the macro language that came with the assembler to invent his own quirky, weird, undocumented and thoroughly impenetrable language. None of the programmers could figure it out. He was lamenting that he'd have to assign a programmer to recreating it from scratch, which would take months and cost a lot of money." (This was related on the passing of Eric Engstrom, who fixed it)
> Macros can be a bad programming idea, but at least they are a fun bad programming idea
Ain't that the truth.
Different programmers use different tools. If the end product is the desired result, then let people use what works best for them. I don't use dynamically typed languages; I avoid them like the plague, but that doesn't mean there isn't a ton of good work being done in Python.
Walter is a brilliant language developer and has a lot of firm opinions of programming semantics. I hope to see him expand upon this post and share why he thinks programmers need to be careful using these constructs.
1) original programmer moves on, someone else maintains/fixes/owns the code
2) scope / features / scale expands over time generally, so bad decisions for extension / expansion / scaling become prominent
But really the article is about 1.
https://www.bleepingcomputer.com/news/security/bouncy-castle...
So I think [] has a place for containers. Overloading * and -> for smart pointers also makes things look a lot nicer. I think the more advanced indexing modes of numpy (and by extension tensorflow) were a mistake though. Those create weird edge cases and would have been much better of as named methods.
Without something more we just have an authority appealing to himself. And all of us commenting with no shared basis for our claims and no path to consensus.
I'm convinced a person like Walter Bright knows a thing or two about software development in general and creating and implementing programming languages in particular.
None of us is as smart as all of us.
I think it's fair enough to do the former, even if i don't agree with them, and likely won't change my mind in this case.
The man makes his living designing and implementing programming languages.
He expressed his opinion on the subject of designing and implementing languages.
Enough with this petty anti-intellectualism. If you have nothing of value to add to the discussion please remain silent. Your ignorance is not comparable to someone else's knowledge and experience. Expressing envy and pettiness regarding other people's knowledge, skill, and professional accomplishments does not pass off as a technical argument. It just sounds petty.
Everyone and their grandmother uses the C Preprocessor. However comparatively few people have heard about its cousin m4 which was invented by the same people. m4 never caught on because it isn't very pleasant to use. I'm surprised no one here has mentioned it, since its existence is living proof of just how successful and amazing the C preprocessor is. https://wolfram.schneider.org/bsd/7thEdManVol2/m4/m4.pdf
https://hinchman-amanda.medium.com/null-pointer-references-t...
Personal preferences and all, I feel the comparison between Result and exceptions is somewhere between misguided and miopic. The Result type and exceptions do not serve the same purpose. Exceptions are not intended to handle control flow, and the Result type is not intended to handle exceptional behavior. Moreover, they are not mutually exclusive.
To provide an example, a Result type would be returned by a database query, and it would return Ok if an element was found or return a Fail if either an element didn't existed or was not valid. Meanwhile an exception would be thrown if a memory allocation error happened somewhere.
I mean, not finding a record in a database is not an error, nor is getting a response to a HTTP request with status code 3xx or a 404 or a 409.
But in some languages, exceptions would be used to handle these cases. Admittedly, those languages are usually higher level than C/C++ (but both Java and Python would probably find it common to handle a 3xx or a 404 or a missing row with an exception)
At a high level, I think the important thing is that funct() -> Result<T> and func() -> T that may throw/raise an exception are basically equivalent. Result may require slightly more explicitness at (at least) one call site, but ultimately both allow you to return a T, or report some non-T result, in a structured way. The only difference is that exceptions force you to handle the result explicitly (although that may just mean a single call to value! or VALUE_OR_DIE, or whatever the "get the T or segfault/panic" method is). In this way, I really do think checked exceptions are highly analogous to Result types.
Implementations may have different performance tradeoffs, but those aren't implicit in the choice of exception vs. result.