January 24, 2022

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've done its counterpart just two days ago by commenting out one line (in somebody else's code):

foreach(i; 0..2)
// foo();

bar();

Oops! Now bar() is executed multiple times.

I am saddened with this skipped-braces "optimization" because all that risk for just to skip writing two characters! Wow! Now... that's... interesting... :) Humans are really interesting...

Ali

Yeah, that's classic! Tbh, either force your users to use curly brackets or find a python like system that will not cause these types of errors. In my language this will be:

for i in 0..2:
 // foo()

bar()

Ending up having an empty "for" statement and executing "bar()" only once.

January 24, 2022
On 1/24/22 08:23, rempas wrote:

> I can understand and agree the cases where people will just make
> mistakes because they are humans

Definitely.

> or because they are coding at a time
> they should be sleeping

That's misleading. Making mistakes is an important part of learning. It happens all the time. That's why we have processes to follow to protect ourselves from ourselves. const, unnecessary (!) curly braces, etc. are parts of such protection.

However, const on the function API is also for communication: It tells the caller what parameters are not going to be mutated by the function. But I've become one of the people who advocate 'in' over 'const' especially when compiled with -preview=in:

  https://dlang.org/spec/function.html#in-params

Sweet! 'in' even enables passing rvalues by reference! :)

Ali

January 24, 2022
On Mon, Jan 24, 2022 at 09:30:26AM -0800, Ali Çehreli via Digitalmars-d wrote:
> On 1/24/22 04:31, Dennis wrote:
[...]
> > One day I had a single-line for-loop body, and to fix an error I quickly added an `import` statement above it, pushing the for-loop body outside the for-loop scope. Oops.
> 
> I've done its counterpart just two days ago by commenting out one line
> (in somebody else's code):
> 
>   foreach(i; 0..2)
>     // foo();
> 
>   bar();
> 
> Oops! Now bar() is executed multiple times.

Huh, for some reason I was under the impression that D does not allow un-braced blocks in a looping construct?  Or maybe it's just self-imposed restriction that became subconscious, probably precisely because of bugs like these.

If-statements are another trap waiting to happen... but so far I've found it very hard to resist the conciseness of:

	if (x == y)
		doSomething();
	else if (y == z)
		doSomethingElse();
	else
		doYetAnotherThing();

as opposed to the verbosity of:

	if (x == y)
	{
		doSomething();
	}
	else if (y == z)
	{
		doSomethingElse();
	}
	else
	{
		doYetAnotherThing();
	}

But in the former, it's too easy to e.g. add another line to the else block and forget the add the braces as well.

For some reason I was under the impression that D didn't allow unbraced loop bodies because in the case of loops such errors could be a lot worse than in if-statements.


T

-- 
What do you get if you drop a piano down a mineshaft? A flat minor.
January 24, 2022

On Monday, 24 January 2022 at 17:55:39 UTC, H. S. Teoh wrote:

>

For some reason I was under the impression that D didn't allow unbraced loop bodies because in the case of loops such errors could be a lot worse than in if-statements.

D doesn't allow a single ; loop body to prevent this mistake:

for (int i=0; i<l; i++);

You have to write {} for an empty body.

It also doesn't allow dangling else, and function bodies need braces {}. Other than that, I don't recall D demanding {}.

January 24, 2022

On Monday, 24 January 2022 at 17:55:39 UTC, H. S. Teoh wrote:

>

If-statements are another trap waiting to happen... but so far I've found it very hard to resist the conciseness of:

That's why I use 'Egyptian braces' with a 'cuddled else':

    if (x == y) {
        doSomething();
    } else if (y == z) {
        doSomethingElse();
    } else {
        doYetAnotherThing();
    }

Not the prettiest, but compact and safe.

January 24, 2022
On 1/24/22 09:55, H. S. Teoh wrote:

> as opposed to the verbosity of:
>
> 	if (x == y)
> 	{
> 		doSomething();
> 	}
> 	else if (y == z)
> 	{
> 		doSomethingElse();
> 	}
> 	else
> 	{
> 		doYetAnotherThing();
> 	}

Luckily, humans are very adaptive and can be happy with the following style. ;)

if (x == y) {
  doSomething();

} else if (y == z) {
  doSomethingElse();

} else {
  doYetAnotherThing();
}

Especially note TABs are only for Makefiles. :p

But I can like the following no-brace formatting as well:

  if      (x == y) doSomething();
  else if (y == z) doSomethingElse();
  else             doYetAnotherThing();

Ali

January 24, 2022
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.
January 24, 2022
On Mon, Jan 24, 2022 at 10:22:47AM -0800, Ali Çehreli via Digitalmars-d wrote:
> On 1/24/22 09:55, H. S. Teoh wrote:
> 
> > as opposed to the verbosity of:
> >
> > 	if (x == y)
> > 	{
> > 		doSomething();
> > 	}
> > 	else if (y == z)
> > 	{
> > 		doSomethingElse();
> > 	}
> > 	else
> > 	{
> > 		doYetAnotherThing();
> > 	}
> 
> Luckily, humans are very adaptive and can be happy with the following style.  ;)
> 
> if (x == y) {
>   doSomething();
> 
> } else if (y == z) {
>   doSomethingElse();
> 
> } else {
>   doYetAnotherThing();
> }

I used to be a big fan of this style, in fact.  But these days, I write in Phobos style -- mainly because at one point I was actively contributing to Phobos and it was just too much of a hassle to have to keep switching mental gears between two divergent styles. After a while, I grew to like the better clarity of the extra whitespace around my code, and stuck to Phobos style ever since.

There are still cases where this whitespace becomes excessive, though, and the above code is an example.  But to mix the two styles in the same code would be even worse, so for now I'm just gritting my teeth over unbraced if-statements. :-D


> Especially note TABs are only for Makefiles. :p

I used to be a big fan of tabs. Developed a whole philosophy around why tabs were superior (mainly based around the 80's now-outdated philosophy of saving every last byte because you only had 64K of RAM so every little bit counts).  These days, I set expandtab in Vim for all code, and am a lot happier for it. :-P

Two-space indentation though... I used to be a big fan of that, in my Perl phase. Needless to say, in retrospect, 2-space indentation + Perl kookiness = completely unreadable code.  These days I prefer 4 spaces. (Well OK, that's also the influence of Phobos style, but it's much easier to tell what level you're at with 4-space indentation than with 2-space. And like Torvalds would say, if you need to indent so deep that it's pushing against the right edge of the screen, you're doing something wrong and should refactor your code to avoid that many levels of nesting in the first place. (He was talking about 8-space tab indentation BTW. Though for D code 8-space indentation is a bit too excessive, given the typical nesting level of idiomatic D code.))


> But I can like the following no-brace formatting as well:
> 
>   if      (x == y) doSomething();
>   else if (y == z) doSomethingElse();
>   else             doYetAnotherThing();
[...]

This really scares me, because it's also extremely easy to make mistakes (e.g., need to add another line to the else block, so wrap the existing line to a new line and append another line -- while forgetting to add braces).


T

-- 
One Word to write them all, One Access to find them, One Excel to count them all, And thus to Windows bind them. -- Mike Champion
January 24, 2022

On 1/24/22 10:44 AM, Walter Bright wrote:

>

On 1/24/2022 6:40 AM, Steven Schveighoffer wrote:

>

immutable/const is purely a compile-time concept. It's not reflected in the final binary, so it's not necessary to forward the attributes to a language that doesn't support it.

Immutable global data gets placed in read-only memory sections. Read-only memory sections are nice in a demand-paged virtual system, as the pages they are in never have to be copied because they are never marked as "dirty".

Sure, but const isn't necessary for that. Const is a compiler construct to prevent you from doing stupid things. But a programming language can run perfectly fine with ROM without having a const concept.

D1 put string literals in ROM without const.

-Steve

January 24, 2022

On 1/24/22 11:27 AM, rempas wrote:

>

Thanks for your time! My question is if you think that it will be very very bad to not include it in my language or not. I'm slowly changing my mind after seeing all these comments and thinking about including it tho...

Some newer languages are gravitating towards const by default.

For example, Swift by default takes parameters as constants. You used to be able to take them with the var designation, but now you have to declare a new variable in the function if you want to have a mutable copy. If you declare a variable with var and don't mutate it, the compiler complains that you really should use let (the const equivalent).

The advantages of const to prevent mistakes definitely exist, and most people appreciate it. That being said I generally don't use it unless I think of it, or I have to for a library to work.

-Steve