May 11, 2022
On 5/11/2022 3:48 AM, IGotD- wrote:
> C# went the other way, no const parameters at all. Maybe because it doesn't make much sense because there is a lot of runtime violations of const underneath. Anyway, I like the C# aproach better because it is simpler for the programmer.

It's not simpler for someone trying to read the code. Function documentation almost never says "by the way, this `getValue` function also tweaks the database."


> Another problem is that all this badging tend to get out of hand and it breeds more badges until it doesn't mean anything. For example what will happen if D adds a mutable attribute?

It already has a mutable attribute - nothing!
May 11, 2022
On 5/11/2022 5:54 AM, jmh530 wrote:
> And then for whatever reason there was a change so that now scope return has to be return scope.

The reason was does "ref scope return" mean return-ref scope, or does it mean ref return-scope?
May 11, 2022
On Wednesday, 11 May 2022 at 18:21:29 UTC, Walter Bright wrote:
> On 5/11/2022 5:54 AM, jmh530 wrote:
>> And then for whatever reason there was a change so that now scope return has to be return scope.
>
> The reason was does "ref scope return" mean return-ref scope, or does it mean ref return-scope?

I get that it was done for a reason...it's just a confusing reason...
May 11, 2022
On 5/11/2022 4:07 PM, jmh530 wrote:
> I get that it was done for a reason...it's just a confusing reason...

The idea was to reduce the confusion!
May 13, 2022

On Wednesday, 11 May 2022 at 18:18:18 UTC, Walter Bright wrote:

>

On 5/11/2022 3:48 AM, IGotD- wrote:

>

C# went the other way, no const parameters at all. Maybe because it doesn't make much sense because there is a lot of runtime violations of const underneath. Anyway, I like the C# aproach better because it is simpler for the programmer.

It's not simpler for someone trying to read the code. Function documentation almost never says "by the way, this getValue function also tweaks the database."

Good documentation will. Really const doesn't help with making code more readable either. I avoided using const in D cause it just got in the way more than it was helping. The most useful feature for const to exist in C++ was that it allows you to do const T& which will allow you to pass anything to that function, rvalue or lvalue without making a copy. D doesn't do this though. I think C# did the right call in excluding it, it would have just added complexity without much benefit.

> >

Another problem is that all this badging tend to get out of hand and it breeds more badges until it doesn't mean anything. For example what will happen if D adds a mutable attribute?

It already has a mutable attribute - nothing!

I think he means a mutable attribute that allows you to modify a value even if the object is const. Like C++'s mutable.

May 13, 2022
On Fri, May 13, 2022 at 06:25:22PM +0000, Fry via Digitalmars-d wrote:
> On Wednesday, 11 May 2022 at 18:18:18 UTC, Walter Bright wrote:
> > On 5/11/2022 3:48 AM, IGotD- wrote:
> > > C# went the other way, no const parameters at all.
[...]
> > It's not simpler for someone trying to read the code. Function documentation almost never says "by the way, this `getValue` function also tweaks the database."
> 
> Good documentation will. Really const doesn't help with making code more readable either. I avoided using const in D cause it just got in the way more than it was helping.

Yeah, more than one long-time D user (including myself) has come to this conclusion. In my own experience, const is mainly useful at the lowest levels of code (strings, leaf-node modules that operate on some self-contained data structure that doesn't depend on anything else). Once you get to a high enough level of abstraction, it starts getting in your way and becomes far too much more work than the benefits it offers; it's just not worth the trouble.  So these days I don't really bother with const, except in small pockets of leaf-node code.


> The most useful feature for const to exist in C++ was that it allows you to do `const T&` which will allow you to pass anything to that function, rvalue or lvalue without making a copy. D doesn't do this though.
[...]

I thought `auto ref` was supposed to do this?  But I recall people hating `auto ref` for various reasons...


T

-- 
Gone Chopin. Bach in a minuet.
May 14, 2022

On Friday, 13 May 2022 at 18:43:11 UTC, H. S. Teoh wrote:

>

On Fri, May 13, 2022 at 06:25:22PM +0000, Fry via Digitalmars-d wrote:

> > >

[...]
[...]
[...]

Yeah, more than one long-time D user (including myself) has come to this conclusion. In my own experience, const is mainly useful at the lowest levels of code (strings, leaf-node modules that operate on some self-contained data structure that doesn't depend on anything else). Once you get to a high enough level of abstraction, it starts getting in your way and becomes far too much more work than the benefits it offers; it's just not worth the trouble. So these days I don't really bother with const, except in small pockets of leaf-node code.

>

[...]
[...]

I thought auto ref was supposed to do this? But I recall people hating auto ref for various reasons...

T

Both auto ref and in make a copy if it's an rvalue.

Idk why people hate auto ref though... most likely the fact that one might have to do __traits(isRef, symbol) anyways for some edge cases

May 13, 2022
On 5/13/22 17:04, Tejas wrote:
> On Friday, 13 May 2022 at 18:43:11 UTC, H. S. Teoh wrote:
>> On Fri, May 13, 2022 at 06:25:22PM +0000, Fry via Digitalmars-d wrote:
>>> > > [...]
>> [...]
>>> [...]
>>
>> Yeah, more than one long-time D user (including myself) has come to
>> this conclusion. In my own experience, const is mainly useful at the
>> lowest levels of code (strings, leaf-node modules that operate on some
>> self-contained data structure that doesn't depend on anything else).
>> Once you get to a high enough level of abstraction, it starts getting
>> in your way and becomes far too much more work than the benefits it
>> offers; it's just not worth the trouble.  So these days I don't really
>> bother with const, except in small pockets of leaf-node code.
>>
>>
>>> [...]
>> [...]
>>
>> I thought `auto ref` was supposed to do this?  But I recall people
>> hating `auto ref` for various reasons...
>>
>>
>> T
>
> Both `auto ref` and `in` make a copy if it's an rvalue.

'in' may not copy rvalues when compiled with -preview=in:

import std.stdio;

struct S(size_t N) {
  int[N] i; // Can be veeery big

  this (int i) {
    writeln("When constructing: ", &this.i);
  }
}

void foo(T)(in T s) {
  writeln("Inside foo       : ", &s.i);
}

void main() {
  foo(S!1(42));
  foo(S!1000(43));
}

foo() receives two rvalue objects, second of which is passed by reference just because the compiler decided to do so:

When constructing: 7FFD44B79EA0
Inside foo       : 7FFD44B79E78  // <-- Copied for N==1; who cares. :)
When constructing: 7FFD44B79EB0
Inside foo       : 7FFD44B79EB0  // <-- rvalue passed by reference.

That prooves D does indeed support rvalue references. :D

> Idk why people hate `auto ref` though... most likely the fact that one
> might have to do `__traits(isRef, symbol)` anyways for some edge cases

My discomfort with 'auto ref' is, it comes with a guideline: Treat the parameter 'const'. If not, an rvalue object would be mutated but the outside world would not know about it (mostly). Why would it be different for lvalues? Because they are passed by reference, the caller has an interest in the mutation, right?

So, I can't imagine a use case where lvalue mutation is important but rvalue mutation is not.

Ali

May 14, 2022

On Saturday, 14 May 2022 at 01:11:03 UTC, Ali Çehreli wrote:

>

So, I can't imagine a use case where lvalue mutation is important but rvalue mutation is not.

Maybe for a function that wants to define optional out parameters that the caller can care about or not? It's a trivial example, but something like:

import std.stdio: writefln;

void foo () (int input,
             auto ref int optional_output = 0 /* dummy rvalue default */)
{
    writefln!"Input received: %s"(input);
    optional_output = input * 2;
}

void main()
{
    foo(7);  // `optional_out` parameter is effectively ignored
    writefln("Output parameter was not used");
    int output;
    foo(19, output);  // `optional_out` parameter writes to lvalue
    writefln!"Output received: %s"(output);
    assert(output == 38);
}

A similar pattern might also be useful for passing optional context object to which a function might write, say, information on its progress.

Not sure that either of these would be wise, but at least they are conceivable options.

May 14, 2022
On Sat, May 14, 2022 at 09:34:51AM +0000, Joseph Rushton Wakeling via Digitalmars-d wrote:
> On Saturday, 14 May 2022 at 01:11:03 UTC, Ali Çehreli wrote:
> > So, I can't imagine a use case where lvalue mutation is important but rvalue mutation is not.
> 
> Maybe for a function that wants to define optional out parameters that the caller can care about or not?  It's a trivial example, but something like:
> 
> ```D
> import std.stdio: writefln;
> 
> void foo () (int input,
>              auto ref int optional_output = 0 /* dummy rvalue default */)
> {
[...]
> }
> ```
> 
> A similar pattern might also be useful for passing optional context object to which a function might write, say, information on its progress.

TBH, for cases like these, I'd just use a pointer and check for null:

	void foo()(int input, int* optional_output = null) {
		...
		if (optional_output !is null)
			*optional_output = ...;
		...
	}

No need to fear pointers, they're in the language for a reason.


T

-- 
Build a man a fire, and he is warm for a night. Set a man on fire, and he is warm for the rest of his life.