www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Is there any real reason to use "const"?

reply rempas <rempas tutanota.com> writes:
Rather than just tell the compiler to not allow me to modify a 
variable (like I don't know what I'm doing with my program), are 
there any reason to use "const" variables?

Other than out of curiosity, I'm actually asking because I'm 
writing a transpiler and I want to know if I should support 
"const" (or the concept of immutability in general) or not. To 
me, "const" has a lot of burdens like:

"should we use it all the time if we don't want to be sure that 
we won't modify a variable or only for critical ones?"

"Then in this case, why not make "const" the default and use 
another word to allow a variable to be mutable (just like Rust 
and other languages)?"

Also another problem is that I don't like the way "const" is 
treated in functions. For example, you cannot past "const" 
variables to functions that take non-const parameters even if we 
are using variables which are ALWAYS copied (which means than 
modifying them will not modify the original value). This is 
stupid because if I want to support "const" for a function, I 
need to make an extra instruction to copy the value to a new 
non-const variable that is created inside the function and make 
an extra instruction. Copying a value is not a slow operation but 
it can be for a big struct. And in any case, it is stupid and it 
pisses me off...

So yeah, I would like to know as soon as possible if there are 
any real reasons to support something like that so I can 
implement it.

What do you guys think?
Jan 24 2022
next sibling parent reply rikki cattermole <rikki cattermole.co.nz> writes:
If you are working with raw pointers, you need a way to express read 
only memory.

Because the CPU does have it, and if you tried to write to it, bye bye 
process.
Jan 24 2022
parent reply rempas <rempas tutanota.com> writes:
On Monday, 24 January 2022 at 10:13:02 UTC, rikki cattermole 
wrote:
 If you are working with raw pointers, you need a way to express 
 read only memory.

 Because the CPU does have it, and if you tried to write to it, 
 bye bye process.
We would try to avoid working with pointers directly in read-only memory but in any case, I still think that the programmer should know what they are doing. The problem you described is why most language use a "string" type that is immutable (in case you point to a string literal) but they also have a way to allow you to modify a "string" type in case you have allocated memory. So it's always up to you. So that's how I see it and I can't see it going wrong. I mean, you will properly read documentation and learn the language right? Well that was sarcastic... no one properly reads documentation these days which is the reason why programmers do all these stuff bringing all the downsides along. I think we should not reward people that don't want to properly learn the language. At the same time I understand that a lot of languages don't have a proper tutorial (book, say it however you want) to teach people about the language. Some don't even have a full language/library reference so even advanced programmer that want to dig cannot learn the language. So they can't blame people for not knowing the language in the end but this is the language creators fault to begin with and I don't plan on doing that. I will make sure that people can have learning the language.
Jan 24 2022
next sibling parent reply Dom DiSc <dominikus scherkl.de> writes:
On Monday, 24 January 2022 at 10:23:14 UTC, rempas wrote:
 The problem you described is why most language use a "string" 
 type that is immutable (in case you point to a string literal) 
 but they also have a way to allow you to modify a "string" type 
 in case you have allocated memory. So it's always up to you.
You need const for functions that need to be able to take a mutable or immutable variable as parameter. And as you don't know which it is, you have to either propagate the constness or make a mutable copy if you want to call a function (depending if the called function has its parameter declared const or not). So better to declare const wherever that is possible, to _avoid_ making useless copies. I would NEVER EVER declare a variable const. It's always either mutable or immutable. I would go as far as forbidding to declare variables const, it's just confusing and useless. const is only for parameters.
Jan 24 2022
next sibling parent Dennis <dkorpel gmail.com> writes:
On Monday, 24 January 2022 at 10:49:36 UTC, Dom DiSc wrote:
 I would NEVER EVER declare a variable const. It's always either 
 mutable or immutable.
I usually mark local variables `const` because it's shorter than `immutable`, and `immutable` doesn't buy me anything over `const` in those cases.
Jan 24 2022
prev sibling next sibling parent reply rempas <rempas tutanota.com> writes:
On Monday, 24 January 2022 at 10:49:36 UTC, Dom DiSc wrote:
 You need const for functions that need to be able to take a 
 mutable or immutable variable as parameter. And as you don't 
 know which it is, you have to either propagate the constness or 
 make a mutable copy if you want to call a function (depending 
 if the called function has its parameter declared const or 
 not). So better to declare const wherever that is possible, to 
 _avoid_ making useless copies.
 I would NEVER EVER declare a variable const. It's always either 
 mutable or immutable. I would go as far as forbidding to 
 declare variables const, it's just confusing and useless. const 
 is only for parameters.
That's what I'm saying. If there were no "const" and it was up to the programmer to decide what to do, we wouldn't had to worry about all that. Is you string pointing to a string literal? Then don't use the "add" function to add more data to it! Or maybe have a member in the "string type" that will check if the string points to a string literal and in this case, allocate memory (for both the old and the new value), copy the old value and then add the new value! This is just so simple and easy for both the creators of the language and the users My point is if there is a true reason to implement "const" itself other than just protecting programmers that haven't read the documentation and are trying to modify a string literal? Any.. "practical" reason so to say.
Jan 24 2022
parent reply Dennis <dkorpel gmail.com> writes:
On Monday, 24 January 2022 at 11:23:38 UTC, rempas wrote:
 My point is if there is a true reason to implement "const" 
 itself other than just protecting programmers that haven't read 
 the documentation and are trying to modify a string literal?
There's this idea among some programmers that to prevent software bugs, we just need more well-trained / competent people, "like myself". This opinion tends to wane over time as you start writing those bugs you thought you'd never write. I never understood the idea of people always including {} braces in if-statements and for-loops. The idea is to prevent this: ```D if (x) y(); z(); ``` What idiot would write something like that, `z()` is obviously not part of the if-statement anymore right? 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. Yesterday I wrote a bug like this: ```D void alphabeta(ref Field field, /*...*/) { // (...) Field fieldCopy = field; field.modify(); // < should be fieldCopy.modify() alphabeta(fieldCopy); // (...) } ``` I was debugging it and noticed that I might as well mark the `field` parameter `const` since it wasn't supposed to change. The next compile I immediately got an error pointing at the bug! Usually I mark variables/functions as strict as possible as early as possible, `const` / `scope` / `pure` / ` safe` etc. I don't know how many bugs I would have written when I wouldn't do that, but it's also comforting knowing that I don't have to worry about accidentally breaking these properties I rely on when I modify a part of the code days or weeks later. Because that's how it gets you: sure, you can do these checks yourself the first time you write a function, but will you do them again every time you make a modification in the future? The compiler can, you likely won't. That said, it can also be annoying marking all function parameters `const` appropriately to convince the compiler that you indeed don't modify a `const` variable. I've heard of C++ people applying a macro `#define const` to a remove all `const` from a const-correct codebase because they don't like dealing with it. If you don't think it's worth it, you can not mark parameters `const` if you also don't mark your variables `const`.
Jan 24 2022
parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 1/24/22 04:31, Dennis wrote:

 I never understood the idea of people always including {} braces in
 if-statements and for-loops.
[...]
 What idiot would write something like that,
Yeah, that thought disappears once one realizes that humans are mistake-making machines. :)
 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. 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
Jan 24 2022
next sibling parent reply rempas <rempas tutanota.com> writes:
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.
Jan 24 2022
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
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.
Jan 24 2022
parent rempas <rempas tutanota.com> writes:
On Monday, 24 January 2022 at 18:48:48 UTC, H. S. Teoh wrote:
 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
I hear you very clearly! Thanks! No seriously... THANKS A LOT!!! I should constantly remind myself that a wise man always learns from mistakes that have made prior to him and always advance! Well I also found about "negation" overflow thanks to LDC2 so yeah, we must have the compiler protect us so "const" will be implemented! Thanks a lot for your time!
Jan 25 2022
prev sibling next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
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.
Jan 24 2022
next sibling parent Dennis <dkorpel gmail.com> writes:
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: ```D 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 {}.
Jan 24 2022
prev sibling next sibling parent Dennis <dkorpel gmail.com> writes:
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': ```D if (x == y) { doSomething(); } else if (y == z) { doSomethingElse(); } else { doYetAnotherThing(); } ``` Not the prettiest, but compact and safe.
Jan 24 2022
prev sibling parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
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
Jan 24 2022
parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
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
Jan 24 2022
prev sibling parent reply Patrick Schluter <Patrick.Schluter bbox.fr> writes:
On Monday, 24 January 2022 at 17:30:26 UTC, Ali Çehreli wrote:
 On 1/24/22 04:31, Dennis wrote:

 I never understood the idea of people always including {}
braces in
 if-statements and for-loops.
[...]
 What idiot would write something like that,
Yeah, that thought disappears once one realizes that humans are mistake-making machines. :)
 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. 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...
Not really a problem in my experience (25 years of C + 5 years of D) it happened only a couple of times and it is quickly found when it happens. I'm an extremly minimalistic programmer, I avoid unecessary parenthesis and brackets whenever I can.
Jan 24 2022
parent rempas <rempas tutanota.com> writes:
On Monday, 24 January 2022 at 20:31:36 UTC, Patrick Schluter 
wrote:
 I'm an extremly minimalistic programmer, I avoid unecessary 
 parenthesis and brackets whenever I can.
Makes sense when your code if finalized and (properly) checked but bugs can be introduced if you haven't finished your code. I find myself doing something like the following: ``` if (val) { one_line_statement(); } else if (other_val) { one_line_statement(); } ``` I think it doesn't take much space and at the same time, it looks very readable.
Jan 25 2022
prev sibling parent vit <vit vit.vit> writes:
On Monday, 24 January 2022 at 10:49:36 UTC, Dom DiSc wrote:
 On Monday, 24 January 2022 at 10:23:14 UTC, rempas wrote:
 The problem you described is why most language use a "string" 
 type that is immutable (in case you point to a string literal) 
 but they also have a way to allow you to modify a "string" 
 type in case you have allocated memory. So it's always up to 
 you.
... I would NEVER EVER declare a variable const. It's always either mutable or immutable. I would go as far as forbidding to declare variables const, it's just confusing and useless. const is only for parameters.
`const` variable are necessary: - in generic code when you don't know if variable is mutable or `immutable`. - if you initialize variable from `const` function parameter or other `const` variables - when you don't want shared variable (`immutable` is implicitly shared, `const` is not). - ...
Jan 24 2022
prev sibling parent reply bauss <jj_1337 live.dk> writes:
On Monday, 24 January 2022 at 10:23:14 UTC, rempas wrote:
 On Monday, 24 January 2022 at 10:13:02 UTC, rikki cattermole 
 wrote:
 If you are working with raw pointers, you need a way to 
 express read only memory.

 Because the CPU does have it, and if you tried to write to it, 
 bye bye process.
We would try to avoid working with pointers directly in read-only memory but in any case, I still think that the programmer should know what they are doing.
Sure thing but that's not realistic if you work professionally as you might be handed code that 15 other people worked on for a decade before you. You will not be able to reason about the memory properly, in which case if certain variables are marked with const it will tell future people that this is read-only memory.
Jan 25 2022
next sibling parent reply rempas <rempas tutanota.com> writes:
On Tuesday, 25 January 2022 at 10:42:35 UTC, bauss wrote:
 Sure thing but that's not realistic if you work professionally 
 as you might be handed code that 15 other people worked on for 
 a decade before you.

 You will not be able to reason about the memory properly, in 
 which case if certain variables are marked with const it will 
 tell future people that this is read-only memory.
Yeah, the problem with me is that I almost thing about the most cases when you work alone or with good people but never cases like the one you described. The others did an amazing job explain that to me. That and the fact that humans do mistakes even when they know what they are doing...
Jan 25 2022
parent bauss <jj_1337 live.dk> writes:
On Tuesday, 25 January 2022 at 11:40:18 UTC, rempas wrote:
 On Tuesday, 25 January 2022 at 10:42:35 UTC, bauss wrote:
 Sure thing but that's not realistic if you work professionally 
 as you might be handed code that 15 other people worked on for 
 a decade before you.

 You will not be able to reason about the memory properly, in 
 which case if certain variables are marked with const it will 
 tell future people that this is read-only memory.
Yeah, the problem with me is that I almost thing about the most cases when you work alone or with good people but never cases like the one you described. The others did an amazing job explain that to me. That and the fact that humans do mistakes even when they know what they are doing...
Yeah but it's not the norm, especially not for big companies where many people come and go all the time.
Jan 25 2022
prev sibling parent reply rempas <rempas tutanota.com> writes:
On Tuesday, 25 January 2022 at 10:42:35 UTC, bauss wrote:
 Sure thing but that's not realistic if you work professionally 
 as you might be handed code that 15 other people worked on for 
 a decade before you.

 You will not be able to reason about the memory properly, in 
 which case if certain variables are marked with const it will 
 tell future people that this is read-only memory.
When saying "we would try to avoid working with read-only memory and pointers directly", I mean the language I'm going to create. So yeah, there will be no code from 10 years ago, I'm pretty sure ;) Aside from the joke, I will keep the fact the a lot of people will work in the same project so having as many protections as possible.
Jan 26 2022
parent rempas <rempas tutanota.com> writes:
On Thursday, 27 January 2022 at 06:26:43 UTC, rempas wrote:
 On Tuesday, 25 January 2022 at 10:42:35 UTC, bauss wrote:
 Sure thing but that's not realistic if you work professionally 
 as you might be handed code that 15 other people worked on for 
 a decade before you.

 You will not be able to reason about the memory properly, in 
 which case if certain variables are marked with const it will 
 tell future people that this is read-only memory.
When saying "we would try to avoid working with read-only memory and pointers directly", I mean the language I'm going to create. So yeah, there will be no code from 10 years ago, I'm pretty sure ;) Aside from the joke, I will keep the fact the a lot of people will work in the same project so having as many protections as possible.
Yep! I answered two times in the same reply! You will only see that from me! The one and only!
Jan 26 2022
prev sibling next sibling parent reply Dennis <dkorpel gmail.com> writes:
On Monday, 24 January 2022 at 10:06:49 UTC, rempas wrote:
 For example, you cannot past "const" variables to functions 
 that take non-const parameters even if we are using variables 
 which are ALWAYS copied (which means than modifying them will 
 not modify the original value).
You can? This compiles: ```D struct S { int x; const(int)[] arr; immutable(char)[] str; } void main() { const S sc; immutable S si; f(sc); // mutable copy passed to f f(si); // mutable copy passed to f } void f(S s) { s.x = 3; } ``` It will only error if the type has mutable indirections, because then you could violate `const` by changing the contents behind the array/pointer.
Jan 24 2022
parent rempas <rempas tutanota.com> writes:
On Monday, 24 January 2022 at 11:15:47 UTC, Dennis wrote:
 On Monday, 24 January 2022 at 10:06:49 UTC, rempas wrote:
 For example, you cannot past "const" variables to functions 
 that take non-const parameters even if we are using variables 
 which are ALWAYS copied (which means than modifying them will 
 not modify the original value).
You can? This compiles: ```D struct S { int x; const(int)[] arr; immutable(char)[] str; } void main() { const S sc; immutable S si; f(sc); // mutable copy passed to f f(si); // mutable copy passed to f } void f(S s) { s.x = 3; } ``` It will only error if the type has mutable indirections, because then you could violate `const` by changing the contents behind the array/pointer.
I thought I tried it out some months ago and it wasn't possible. Well, turns out that I was wrong about the second part of my post. And that I'm an idiot of course but this is not something I didn't knew before neither something that will stop me....
Jan 24 2022
prev sibling next sibling parent Mark <smarksc gmail.com> writes:
On Monday, 24 January 2022 at 10:06:49 UTC, rempas wrote:
 Rather than just tell the compiler to not allow me to modify a 
 variable (like I don't know what I'm doing with my program), 
 are there any reason to use "const" variables?

 Other than out of curiosity, I'm actually asking because I'm 
 writing a transpiler and I want to know if I should support 
 "const" (or the concept of immutability in general) or not. To 
 me, "const" has a lot of burdens like:

 "should we use it all the time if we don't want to be sure that 
 we won't modify a variable or only for critical ones?"

 "Then in this case, why not make "const" the default and use 
 another word to allow a variable to be mutable (just like Rust 
 and other languages)?"

 Also another problem is that I don't like the way "const" is 
 treated in functions. For example, you cannot past "const" 
 variables to functions that take non-const parameters even if 
 we are using variables which are ALWAYS copied (which means 
 than modifying them will not modify the original value). This 
 is stupid because if I want to support "const" for a function, 
 I need to make an extra instruction to copy the value to a new 
 non-const variable that is created inside the function and make 
 an extra instruction. Copying a value is not a slow operation 
 but it can be for a big struct. And in any case, it is stupid 
 and it pisses me off...

 So yeah, I would like to know as soon as possible if there are 
 any real reasons to support something like that so I can 
 implement it.

 What do you guys think?
AFAIK, const is a contentious topic. You can find many previous discussions on this forum and elsewhere revolving around the questions of how to use const, whether D's const is generally useful, the effect it has on APIs, whether we need tail-const/head-mutable in the language, etc. Jonathan M Davis wrote an article which covers some of these questions (and also considers C++'s const): http://jmdavisprog.com/articles/why-const-sucks.html The article is 4 years old but I don't think much has changed since then. I know that Rust also has const (by default; non-const variable are marked with `mut`) but I don't have enough experience to give an honest opinion on it.
Jan 24 2022
prev sibling next sibling parent reply Guillaume Piolat <first.last gmail.com> writes:
On Monday, 24 January 2022 at 10:06:49 UTC, rempas wrote:
 What do you guys think?
const has utility at interface boundaries. You don't want an API to be used incorrectly. If it's internal code though, it only helps the maintainers, and is often line noise.
Jan 24 2022
parent reply rempas <rempas tutanota.com> writes:
On Monday, 24 January 2022 at 14:22:55 UTC, Guillaume Piolat 
wrote:
 const has utility at interface boundaries. You don't want an 
 API to be used incorrectly.
 If it's internal code though, it only helps the maintainers, 
 and is often line noise.
I mean, I can understand and agree the cases where people will just make mistakes because they are humans or because they are coding at a time they should be sleeping but like I said, I think that why should start writing good documentation and people should start reading it. Thanks for your thoughts!
Jan 24 2022
next sibling parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
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
Jan 24 2022
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 24.01.22 18:47, Ali Çehreli wrote:
 
 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! :)
Actually I am very disappointed that passing rvalues by ref is now tied to transitive const. Makes no sense.
Jan 24 2022
prev sibling parent reply Patrick Schluter <Patrick.Schluter bbox.fr> writes:
On Monday, 24 January 2022 at 16:23:25 UTC, rempas wrote:
 On Monday, 24 January 2022 at 14:22:55 UTC, Guillaume Piolat 
 wrote:
 const has utility at interface boundaries. You don't want an 
 API to be used incorrectly.
 If it's internal code though, it only helps the maintainers, 
 and is often line noise.
I mean, I can understand and agree the cases where people will just make mistakes because they are humans or because they are coding at a time they should be sleeping but like I said, I think that why should start writing good documentation and people should start reading it. Thanks for your thoughts!
No, at API level it is also a documentation help. When a parameter is marked as const, the code reader can be sure that there are no side effects in that function on that parameter. This reduces the mental burden when one tries to read code.
Jan 24 2022
parent rempas <rempas tutanota.com> writes:
On Monday, 24 January 2022 at 20:26:03 UTC, Patrick Schluter 
wrote:
 No, at API level it is also a documentation help. When a 
 parameter is marked as const, the code reader can be sure that 
 there are no side effects in that function on that parameter. 
 This reduces the mental burden when one tries to read code.
We could also had a library reference documentation that explains that in the worse case. But yeah, "const" works better for this
Jan 25 2022
prev sibling next sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 1/24/22 5:06 AM, rempas wrote:
 Rather than just tell the compiler to not allow me to modify a variable 
 (like I don't know what I'm doing with my program), are there any reason 
 to use "const" variables?
If you want the compiler to help you prevent mistakes, yes. If you don't, then no. Other than that, if you want to write a library and have it most accessible to others that may prefer to use const, you should use const, as it will allow the most usage.
 Other than out of curiosity, I'm actually asking because I'm writing a 
 transpiler and I want to know if I should support "const" (or the 
 concept of immutability in general) or not.
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. Same thing with types (see for instance, TypeScript compiled to JavaScript). -Steve
Jan 24 2022
next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
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".
Jan 24 2022
next sibling parent reply rempas <rempas tutanota.com> writes:
On Monday, 24 January 2022 at 15:44:54 UTC, Walter Bright wrote:
 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".
Read only memory? So in the same place where string literals are placed? This sounds cool and it's really something considerable! Does this offer things like security as date will be able to be created once and not get modified again? I hope I'm not asking for too much...
Jan 24 2022
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 1/24/2022 8:43 AM, rempas wrote:
 Read only memory? So in the same place where string literals are placed? This 
 sounds cool and it's really something considerable! Does this offer things
like 
 security as date will be able to be created once and not get modified again? I 
 hope I'm not asking for too much...
You can see this if you run an object file disassembler over the compiler output, the immutable data goes in read-only sections.
Jan 24 2022
next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Mon, Jan 24, 2022 at 12:02:11PM -0800, Walter Bright via Digitalmars-d wrote:
 On 1/24/2022 8:43 AM, rempas wrote:
 Read only memory? So in the same place where string literals are
 placed?  This sounds cool and it's really something considerable!
 Does this offer things like security as date will be able to be
 created once and not get modified again? I hope I'm not asking for
 too much...
You can see this if you run an object file disassembler over the compiler output, the immutable data goes in read-only sections.
It depends on OS support, obviously. But most commonly-used OSes ought to support this. Placing static string data in the read-only segment could be one line of defense against exploits that, e.g., modify an embedded shell script to do something pernicious instead. T -- Elegant or ugly code as well as fine or rude sentences have something in common: they don't depend on the language. -- Luca De Vitis
Jan 24 2022
prev sibling parent rempas <rempas tutanota.com> writes:
On Monday, 24 January 2022 at 20:02:11 UTC, Walter Bright wrote:
 You can see this if you run an object file disassembler over 
 the compiler output, the immutable data goes in read-only 
 sections.
That's actually really amazing and IMO, it is how it should be. Real immutability makes sense rather than just the compiler not letting you mutate the value.
Jan 25 2022
prev sibling next sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
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
Jan 24 2022
parent Walter Bright <newshound2 digitalmars.com> writes:
On 1/24/2022 11:09 AM, Steven Schveighoffer wrote:
 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.
Yeah, but it's undefined behavior if you write to them.
Jan 24 2022
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 1/24/2022 7: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".
Also, immutable data can be merged together, reducing the memory footprint. The dmd compiler does this with strings, for example. Identical string literals are set up to get merged at link time, this can only happen if they are immutable. Immutable zero initialized data also gets merged. A zero is a zero, no matter what type it is!
Jan 24 2022
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Mon, Jan 24, 2022 at 11:59:37AM -0800, Walter Bright via Digitalmars-d wrote:
 On 1/24/2022 7:44 AM, Walter Bright wrote:
[...]
 Also, immutable data can be merged together, reducing the memory
 footprint.  The dmd compiler does this with strings, for example.
 Identical string literals are set up to get merged at link time, this
 can only happen if they are immutable.
 
 Immutable zero initialized data also gets merged. A zero is a zero, no
 matter what type it is!
Makes me curious: how feasible is it to have functions with identical bodies merge together as well? This would help reduce template bloat when you have e.g. a templated type where a bunch of functions are identical because they either don't depend on the type (e.g. a container method that doesn't care about what type the payload is), or else depend on type traits that are common across many types (e.g., int.sizeof which is shared with uint.sizeof, float.sizeof, etc.). T -- Why is it that all of the instruments seeking intelligent life in the universe are pointed away from Earth? -- Michael Beibl
Jan 24 2022
next sibling parent reply jmh530 <john.michael.hall gmail.com> writes:
On Monday, 24 January 2022 at 20:09:46 UTC, H. S. Teoh wrote:
 [snip]

 Makes me curious: how feasible is it to have functions with 
 identical bodies merge together as well? This would help reduce 
 template bloat when you have e.g. a templated type where a 
 bunch of functions are identical because they either don't 
 depend on the type (e.g. a container method that doesn't care 
 about what type the payload is), or else depend on type traits 
 that are common across many types (e.g., int.sizeof which is 
 shared with uint.sizeof, float.sizeof, etc.).


 T
This reminds me of generics. In languages with generics, you can get one copy of a function over many different types. My (possibly incorrect) recollection is that it works like having the compiler cast the generic MyClass!T to MyClass. Ideally from D's perspective there would be a way to do this to minimize any overhead that you would get in Java from being forced to use classes. Merging functions with identical bodies is basically the problem that inout is trying to solve. A way to express inout via the language would probably be good, though inout is rather complicated. For instance, you have an Inout(T) with the property that if you have a function that takes an Inout(T), then calling it with Inout!int, Inout!(const(int)), and Inout!(immutable(int)) would all have only one version of a function at runtime. You would still need to be able to enforce that Inout(T) has the same unique behaviors as inout, such as that you can't modify the variable in the body of the function. This would also be useful for reducing template bloat with allocators. For instance, if you have a templated function that takes an allocator as a parameter, then for every different allocator you use with it, you get an extra copy of the function, even if the body of the function may be the same across the different allocators used (since it may be jumping to the allocator to call that code).
Jan 24 2022
parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Mon, Jan 24, 2022 at 09:41:28PM +0000, jmh530 via Digitalmars-d wrote:
 On Monday, 24 January 2022 at 20:09:46 UTC, H. S. Teoh wrote:
[...]
 Makes me curious: how feasible is it to have functions with
 identical bodies merge together as well? This would help reduce
 template bloat when you have e.g. a templated type where a bunch of
 functions are identical because they either don't depend on the type
 (e.g. a container method that doesn't care about what type the
 payload is), or else depend on type traits that are common across
 many types (e.g., int.sizeof which is shared with uint.sizeof,
 float.sizeof, etc.).
[...]
 This reminds me of generics. In languages with generics, you can get
 one copy of a function over many different types. My (possibly
 incorrect) recollection is that it works like having the compiler cast
 the generic MyClass!T to MyClass. Ideally from D's perspective there
 would be a way to do this to minimize any overhead that you would get
 in Java from being forced to use classes.
IMNSHO, Java generics are weaksauce because they are unable to take advantage of compile-time type information. Basically, once you insert an object of type T into the container, all information about T is erased at runtime, it's just a container of Object, so you couldn't, for example, optimize your container based on the size/alignment of its contents, for example, or use a more compact storage method by inspecting the size of T. The container code can only perform operations that don't introduce a runtime dependency on the specifics of T. D's templates are much more powerful, but that power does come at the price of (sometimes great) template bloat: you get a new copy of the code for every T the template is instantiated with. The ideal best of both worlds is if the D compiler can somehow selectively type-erase the implementation of a template, so that the parts that can be factored out as generic code that works with all T, of which we only need a single instantiation, vs. the type-dependent (non-type-erased) parts which remain as separate instantiations. Merging functions that are binary-identical despite being, at the language level, distinct template instantiations, would be a good step in this direction.
 Merging functions with identical bodies is basically the problem that
 inout is trying to solve. A way to express inout via the language
 would probably be good, though inout is rather complicated.  For
 instance, you have an Inout(T) with the property that if you have a
 function that takes an Inout(T), then calling it with Inout!int,
 Inout!(const(int)), and Inout!(immutable(int)) would all have only one
 version of a function at runtime. You would still need to be able to
 enforce that Inout(T) has the same unique behaviors as inout, such as
 that you can't modify the variable in the body of the function.
inout is a hack and a crock. I think it's the wrong approach. First of all, inout as currently implemented is incomplete: there are a lot of things you cannot express wrt. inout that you might reasonably want to. For example, inout applied to delegates: it quickly becomes ambiguous what the inout is supposed to refer to: the return type of the delegate, or its parameter, or the outer function's return type, or the outer function's parameter. Inside the function body if you need to hold references to the delegate and/or its parameters, it quickly becomes a total mess (and just plain doesn't compile because the compiler doesn't understand what you're trying to do and the language doesn't let you express what you want to do). Secondly, inout applies only to const/immutable. In generic code, I frequently find myself wishing for inout(nothrow), inout(pure), etc., but the language currently does not support such things. And it's also questionable whether attribute soup + attribute soup with complete sub-grammars is really the right direction to go. Your function declarations quickly drowns in attribute subgrammar and readability goes out the window. Third, inout kinda-sorta behaves like a template except that it isn't one, and it kinda-sorta behaves like Java generics, but without half of the expressiveness. It's a special case of templates with the optimization of identical bodies being merged, but artificially restricted to a single function and to alternation between const/mutable/immutable, and arbitrarily *not* a template so it behaves differently from the template part of the language. A misfit stuck in a very narrow niche that fits in neither with templates nor with generics. IMO the right approach is to just replace inout with templates, let the compiler merge identical function bodies and eliminate template bloat, and let the compiler infer the attribute soup for you so that you don't have to deal with it directly.
 This would also be useful for reducing template bloat with allocators.
 For instance, if you have a templated function that takes an allocator
 as a parameter, then for every different allocator you use with it,
 you get an extra copy of the function, even if the body of the
 function may be the same across the different allocators used (since
 it may be jumping to the allocator to call that code).
Exactly. T -- Skill without imagination is craftsmanship and gives us many useful objects such as wickerwork picnic baskets. Imagination without skill gives us modern art. -- Tom Stoppard
Jan 24 2022
prev sibling next sibling parent Walter Bright <newshound2 digitalmars.com> writes:
On 1/24/2022 12:09 PM, H. S. Teoh wrote:
 Makes me curious: how feasible is it to have functions with identical
 bodies merge together as well? This would help reduce template bloat
 when you have e.g. a templated type where a bunch of functions are
 identical because they either don't depend on the type (e.g. a container
 method that doesn't care about what type the payload is), or else depend
 on type traits that are common across many types (e.g., int.sizeof which
 is shared with uint.sizeof, float.sizeof, etc.).
It's quite feasible. I've considered adding that optimization many times.
Jan 24 2022
prev sibling parent Paul Backus <snarwin gmail.com> writes:
On Monday, 24 January 2022 at 20:09:46 UTC, H. S. Teoh wrote:
 Makes me curious: how feasible is it to have functions with 
 identical bodies merge together as well?
Cursory reseach suggests that the main obstacle to this is function pointers. Specifically, if two different functions with identical bodies both have their addresses taken, and the resulting pointers are compared with ==, the result must be `false`. Unless the compiler (or linker, in the case of LTO) can prove that a comparison like this never happens, the optimization is invalid. An easier optimization is to "factor out" the bodies of identical functions into a single new function, and have the original functions simply forward to the new one. For example, when presented with int f(int x, int y) { return x^^2 - y + 42; } int g(int x, int y) { return x^^2 - y + 42; } ...the compiler (or linker) could emit something like int f(int x, int y) { return __generated(x, y); } int g(int x, int y) { return __generated(x, y); } int __generated(int x, int y) { return x^^2 - y + 42; } With tail-call optimization, the overhead of the additional function call is extremely small.
Jan 24 2022
prev sibling parent reply rempas <rempas tutanota.com> writes:
On Monday, 24 January 2022 at 14:40:33 UTC, Steven Schveighoffer 
wrote:
 On 1/24/22 5:06 AM, rempas wrote:
 Rather than just tell the compiler to not allow me to modify a 
 variable (like I don't know what I'm doing with my program), 
 are there any reason to use "const" variables?
If you want the compiler to help you prevent mistakes, yes. If you don't, then no. Other than that, if you want to write a library and have it most accessible to others that may prefer to use const, you should use const, as it will allow the most usage.
 Other than out of curiosity, I'm actually asking because I'm 
 writing a transpiler and I want to know if I should support 
 "const" (or the concept of immutability in general) or not.
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. Same thing with types (see for instance, TypeScript compiled to JavaScript). -Steve
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...
Jan 24 2022
parent Steven Schveighoffer <schveiguy gmail.com> writes:
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
Jan 24 2022
prev sibling next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 1/24/2022 2:06 AM, rempas wrote:
 Also another problem is that I don't like the way "const" is treated in 
 functions. For example, you cannot past "const" variables to functions that
take 
 non-const parameters even if we are using variables which are ALWAYS copied 
Not sure what you mean. This compiles: int foo(const int); int bar(int i) { return foo(i); }
Jan 24 2022
parent reply rempas <rempas tutanota.com> writes:
On Monday, 24 January 2022 at 15:40:56 UTC, Walter Bright wrote:
 Not sure what you mean. This compiles:

   int foo(const int);

   int bar(int i)
   {
     return foo(i);
   }
Actually this compiles but what I thought that didn't was something like this: ``` void mul_num(int num) { num *= 2; } void main() { const int number = 10; mul_num(number); } ``` But it turns out that it compiles but I would swear that I tried something similar to this and it wouldn't compile on both D and C.
Jan 24 2022
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 1/24/22 11:41 AM, rempas wrote:
 On Monday, 24 January 2022 at 15:40:56 UTC, Walter Bright wrote:
 Not sure what you mean. This compiles:

   int foo(const int);

   int bar(int i)
   {
     return foo(i);
   }
Actually this compiles but what I thought that didn't was something like this: ``` void mul_num(int num) {   num *= 2; } void main() {   const int number = 10;   mul_num(number); } ``` But it turns out that it compiles but I would swear that I tried something similar to this and it wouldn't compile on both D and C.
I know exactly what you were doing: ```d void mul_num(T)(T num) { num *= 2; } ``` That will fail if you just do `mul_num(number)` because templates infer the type from the argument (e.g. T becomes `const int`) Unfortunately, D doesn't have a "tail-const" modifier, which would work really well here. -Steve
Jan 24 2022
parent rempas <rempas tutanota.com> writes:
On Monday, 24 January 2022 at 19:15:42 UTC, Steven Schveighoffer 
wrote:
 I know exactly what you were doing:

 ```d
 void mul_num(T)(T num) {
    num *= 2;
 }
 ```

 That will fail if you just do `mul_num(number)` because 
 templates infer the type from the argument (e.g. T becomes 
 `const int`)

 Unfortunately, D doesn't have a "tail-const" modifier, which 
 would work really well here.

 -Steve
Yeah right, now I remember! I did it about 2 months ago in a template function that converts types and It didn't compiled. Now, I know why, thank you!
Jan 25 2022
prev sibling parent forkit <forkit gmail.com> writes:
On Monday, 24 January 2022 at 10:06:49 UTC, rempas wrote:
 ..
 What do you guys think?
Well, if const were default, you could ask yourself, is there any real reason to use mutable :-) If I see a function: void getuser(string pwd){} .. I get kinda concerned. If I write a function: void getuser(string pwd){} .. I also get kinda concerned. After hours of debugging, you eventually realise the root of your problem, was that pwd was not const. Constraints on parameters help to ensure correctness. That is the value of const. I wish safe and const were default actually.
Jan 24 2022