H. S. Teoh 
Posted in reply to rempas
| On Mon, Jan 24, 2022 at 05:38:45PM +0000, rempas via Digitalmars-d wrote:
> On Monday, 24 January 2022 at 17:30:26 UTC, Ali Çehreli wrote:
> > Yeah, that thought disappears once one realizes that humans are mistake-making machines. :)
> >
>
> Yeah, really working towards accepting that but I'm getting closer and closer each day...
I used to be a hardcore C programmer. So hardcore that I won an award in the IOCCC once (well OK, that's not something to be proud of :-D). Correctly answered a C question on an interview technical exam that even my interviewer got wrong. Was totally into the philosophy of "the programmer knows better, compiler please step aside and stop trying to restrict me". Believed my code was perfect, and could not possibly have any bugs because I mulled over every line and polished every character. Didn't believe in test suites because I hand-tested every function when I wrote it so there can't have been any bugs left. And besides, test suites are too cumbersome to use. Used to pride myself on my programs never crashing. (And the times they did I blamed on incidental factors, like I was too distracted because some idiot was WRONG on the internet, gosh the injustice!)
Then I discovered D. And in particular, D's unittest blocks. Was very resistant at first (why would I need to test perfect code), but they were just so darned convenient (unlike unittest frameworks in other languages) they just keep staring at me with puppy eyes until I felt so ashamed for not using them. Then the unittests started catching bugs. COPIOUS bugs. All kinds of boundary cases, careless typos, logic flaws, etc., in my "perfect" code. And EVERY SINGLE TIME I modified a function, another unittest started failing on a previously-tested case (that I disregarded as having nothing to do with my change so not worthy of retesting).
Then this awful realization started dawning on me... my code was NOT perfect. In fact, it was anything BUT perfect. My "perfect" logic that flowed from my "perfect" envisioning of the perfect algorithm was actually full of flaws, logic errors, boundary cases I hadn't thought of, typos, and just plain ole stupid mistakes. And worst of all, *I* was the one making these careless mistakes, practically EVERY SINGLE TIME I wrote any code. What I thought was perfect code was in fact riddled with hidden bugs in almost every line. Usually in lines that I'd written tens of thousands of times throughout my career, that I thought I could write them perfectly even in my dreams, I knew them so well. But it was precisely because of my confidence that these "trivial" lines of code were correct, that I neglected to scrutinize them, and bugs invariably crept in.
Then I observed top C coders in my company make these very same mistakes, OVER AND OVER AGAIN. These were not inexperienced C greenhorns who didn't know what they were doing; these were top C hackers who have been at it for many decades. Yet they were repeating the same age-old mistakes over and over again. I began to realize that these were not merely newbie mistakes that would go away with experience and expertise. These mistakes keep getting made because HUMANS MAKE MISTAKES. And because C's philosophy is to trust the programmer, these mistakes slip into the code unchecked, causing one disaster after another. Buffer overflow here, security exploit there, careless typos that cause the customer's production server to blow up at a critical time. Memory leaks and file descriptor leaks that brought a top-of-the-line server to its knees after months of running "perfectly". And the time and money spent in finding and fixing these bugs were adding up to a huge mountain of technical debt.
Today, my "trust the programmer" philosophy has been shattered. I *want* the compiler to tell me when I'm doing something that looks suspiciously like a mistake. I *want* the language to be safe by default, and I have to go out of my way to commit a mistake. I want the compiler to stop me from doing stupid things that I'd done a hundred times before but continue to do it BECAUSE HUMANS ARE FALLIBLE.
Of course, I don't want to write in a straitjacket like Java makes you do -- there has to be an escape hatch for when I *do* know what I'm doing. But the *default* should be the compiler stopping me from doing stupid things. If I really meant to cast that pointer, I *want* to have to write a verbose, ugly-looking "cast(NewType*) ptr" instead of just having a void* implicitly convert to whatever pointer type I happen to have on hand -- writing out this verbose construct this forces me to stop and think twice about what I'm doing, and hopefully catch any wrong assumptions before it slips into the code. I *want* the compiler to tell me "hey, you said that data was const, and now you're trying to modify it!", which would cause me to remember "oh yeah, I *did* decide 2 months ago that this data should not be changed, and that other piece of code in this other module is relying on this -- why am I trying to modify it now?!".
As Walter often says, programming by convention doesn't work. Decades of catastrophic failures in C code have more than proven this. Humans are fallible, and cannot be relied on for program correctness. We're good at certain things -- leaps of intuition and clever out-of-the-box solutions for hard problems. But for other things, like keeping bugs out of our code, we need help. We need things to be statically verifiable by the compiler to prove that our assumptions indeed hold (and that somebody -- namely ourselves 3 months after writing that code -- didn't violate this assumption and introduce a bug during a last-minute code change before the last release deadline). Weak sauce like C++'s const that can freely be cast away with no consequences anytime you feel like it, will not do. You *need* something strong like D's const to keep the human error in check. Something that the compiler can automatically check and provide real guarantees for.
T
--
Bare foot: (n.) A device for locating thumb tacks on the floor.
|