March 31, 2019
On 3/31/2019 5:35 PM, Rubn wrote:
> you can literally use it everywhere else you can use a type.

No, you can't. An array of refs won't compile, either.

  void test(int& a[]); // error

A C++ ref can only appear at the top of a type AST, which is unlike any other type. Which exactly matches the only place a storage class can be!

March 31, 2019
On Sun, Mar 31, 2019 at 5:20 PM Walter Bright via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
>
> On 3/31/2019 3:48 PM, Manu wrote:
> >> Even in C++ despite of all its silliness, T& is a type and can be used as a template argument.
> >>
> >> Can the language be changed to resolve this problem?
> >
> > 1. I couldn't possibly agree with you more!!! Sadly that's not how it is.
>
> Although ref can be captured with C++ templates, it isn't really a type. For example, you cannot create a pointer to a ref. Furthermore, the spec for it is crammed with wacky special cases to make it behave like a storage class instead of a type. I defy anyone to give a list of cases when C++ ref is inferred and when it is not, and if anyone did, I'm sure it would be wrong. Yes, it's that bad.

Are you saying that it's impossible to design a solution where ref is
part of the type and that's less complex than C++?
Is the complexity is that ecosystem inherent, or is it somewhat
informed by backwards-compatibility, and other C++ design constraints?

The fact that I can write `struct Ref(T)` and that it propagates the type system in a natural way suggests to me that the design is not impossible.


> > Writing that code is like stabbing myself in the hands with rusty forks...
>
> I'm convinced that if C++ ref didn't exist, and we added it to D, you'd hate it :-)

I think you probably have me backwards... irrespective of C++, and
there was some discussion about ref vs pointers, I would push 110% for
ref and eliminate pointers entirely.
I would be much happier to write this hack in the event I need a
pointer, than to hack around ref as I do today:

struct Pointer(T)
{
  size_t addr;
  this(ref T val) { addr = __traits(addressOf, val); }
  ref T get() { return __traits(addressAsRef, T, addr); }
  alias get this;

  Pointer!T opBinary(string op : "+")(size_t offset) { Pointer!T r;
r.addr = addr + offset * T.sizeof; return r; }
  // etc...
}

Pointers are mostly useless and unsafe at best, especially when arrays are a first-class type.


> > I feel emotionally unstabilised, but it can solve your problems in various applications.
>
> Please show a use case. I can't think of one.

A use case for propagating ref in the type? You can't imagine why a template argument might want to distinguish `int` and `ref int`?

I don't know how you've never found yourself static-if-ing on ref-ness
of stuff in every bit of functional meta you've ever written. Ref
creates an out-of-language (we have no language features to interact
with 'storage class') suite of conditions that I frequently have to
wrangle my way through.
We have strong language mechanisms to reason about the type system,
there is no language to reason about storage class that's not a kludge
of awkward hacks and ugly meta utilities.
March 31, 2019
On Sun, Mar 31, 2019 at 6:20 PM Walter Bright via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
>
> On 3/31/2019 5:35 PM, Rubn wrote:
> > you can literally use it everywhere else you can use a type.
>
> No, you can't. An array of refs won't compile, either.
>
>    void test(int& a[]); // error

It could though with some innovative design, and that would be *awesome*.

> A C++ ref can only appear at the top of a type AST, which is unlike any other type. Which exactly matches the only place a storage class can be!

So, what you're saying is, C++ was able to implement storage class semantics, but without distinguishing storage class from the type, thereby making it accessible to any type construction or introspection machinery that the language has available?

Apparently C++ was genius in this way; why would you invent storage class specifically to exclude ref from the ability to interact with type construction and introspection machinery? ref sucks because it can't interact with type construction or introspection. Every meta-interaction with ref is via kludgey hacks.

You mention the complexity of ref in C++, but that needs to be balanced against the entire concept of 'storage class' in D, and literally every single rule relating to it, since that's the counter-weight. If you take the aggregate of all rules and semantics related to 'storage class' in D, especially when including consideration that we have lost the ability to do type construction or introspection in storage classes, is that REALLY simpler than C++?
April 01, 2019
On Friday, 29 March 2019 at 16:06:09 UTC, Victor Porton wrote:
>
> Can the language be changed to resolve this problem?

Currently working on a project that works with a lot on pointers to fixed length arrays and its currently very easy to forget to de-refrence the pointer because the pointer and array syntax is the same.

For example I have code that looks something like this:

---

    float[32]* myarray = magicFunction();

    foreach(el; myarray[0]){

    }
---

But something like this would me much less prone to error (ie missing the [0] above):

---

    ref float[32] myarray = magicFunction();

    foreach(el; myarray){

    }
---

If i convert the above code to use slices I loose the compile-time bounds check.
April 01, 2019
On 3/31/2019 6:30 PM, Manu wrote:
>> A C++ ref can only appear at the top of a type AST, which is unlike any other
>> type. Which exactly matches the only place a storage class can be!
> 
> So, what you're saying is, C++ was able to implement storage class
> semantics, but without distinguishing storage class from the type,
> thereby making it accessible to any type construction or introspection
> machinery that the language has available?
> 
> Apparently C++ was genius in this way; why would you invent storage
> class specifically to exclude ref from the ability to interact with
> type construction and introspection machinery? ref sucks because it
> can't interact with type construction or introspection. Every
> meta-interaction with ref is via kludgey hacks.

Um, have you looked at the special, kludgey rules for refs that permeate the C++ spec? Just about every rule for types has a special case for refs. For example, C++ does type inference in various contexts. Sometimes the ref is inferred, sometimes it is skipped. Can you describe when it is inferred and when it isn't, and why?


> You mention the complexity of ref in C++, but that needs to be
> balanced against the entire concept of 'storage class' in D, and
> literally every single rule relating to it, since that's the
> counter-weight. If you take the aggregate of all rules and semantics
> related to 'storage class' in D, especially when including
> consideration that we have lost the ability to do type construction or
> introspection in storage classes, is that REALLY simpler than C++?

Yes. Keep in mind that I implemented ref in C++, every last gory detail. I've earned my opinion of it.

April 01, 2019
On 3/31/2019 6:19 PM, Manu wrote:
> Are you saying that it's impossible to design a solution where ref is
> part of the type and that's less complex than C++?

I couldn't think of a way. I could have simply copied the C++ design. But why copy a nightmare of kludges?


> Is the complexity is that ecosystem inherent, or is it somewhat
> informed by backwards-compatibility, and other C++ design constraints?

There was no backwards compatibility issue when ref was designed. It's just a poor choice (like volatile).


> The fact that I can write `struct Ref(T)` and that it propagates the
> type system in a natural way suggests to me that the design is not
> impossible.

I have no idea why you want to do such things, but apparently you have a solution that works for you, so that's all good.


>> Please show a use case. I can't think of one.
> A use case for propagating ref in the type? You can't imagine why a
> template argument might want to distinguish `int` and `ref int`?

Indulge me. Why would it?


> I don't know how you've never found yourself static-if-ing on ref-ness
> of stuff in every bit of functional meta you've ever written. Ref
> creates an out-of-language (we have no language features to interact
> with 'storage class') suite of conditions that I frequently have to
> wrangle my way through.
> We have strong language mechanisms to reason about the type system,
> there is no language to reason about storage class that's not a kludge
> of awkward hacks and ugly meta utilities.

If I was faced with that, I'd step back and re-assess why the design required such machinations.

It reminds me of when I peruse Phobos, I'm disturbed by the overly complex code there - something Andrei referred to in the "generality creep" thread.

If C++ does it better, I don't see how. I find the implementation code of the STL to be horrifying in its complexity. I don't know how to write such code without thinking "something has gone terribly wrong".

If I may, I once talked to a Ferrari mechanic. He said that most engines inside, where customers didn't see the innards, were sloppily made. But that the Ferrari engine was a thing of beauty inside and a joy to work on. Ironically, Ferraris are also known for cheap, poorly done cab interiors. An unusual focus for a car company :-) but one that appeals to my inner engineer. The engine in my Dodge was built the same way, it's blueprinted with all top quality parts inside it, but the outside is plain jane, no chrome parts, no dress-up, etc.

April 01, 2019
On 01.04.19 05:38, David Bennett wrote:
> ---
> 
>      float[32]* myarray = magicFunction();
> 
>      foreach(el; myarray[0]){
> 
>      }
> ---

Aside: Is there are reason why you're writing `myarray[0]` instead of `*myarray`? Looks odd.

> But something like this would me much less prone to error (ie missing the [0] above):
> 
> ---
> 
>      ref float[32] myarray = magicFunction();
> 
>      foreach(el; myarray){
> 
>      }
> ---

If you simply forget the `[0]` (or `*`), the code doesn't compile. So that specific mistake can't do any real damage. Doesn't seem like a good example to motivate making the language more complex.
April 01, 2019
On Monday, 1 April 2019 at 01:18:44 UTC, Walter Bright wrote:
> On 3/31/2019 5:35 PM, Rubn wrote:
>> you can literally use it everywhere else you can use a type.
>
> No, you can't. An array of refs won't compile, either.
>
>   void test(int& a[]); // error
>
> A C++ ref can only appear at the top of a type AST, which is unlike any other type. Which exactly matches the only place a storage class can be!

Seriously? You should know as well as I do what that function actually translates to:

   void test(int&* const a);

So I ask you now, how do you get a pointer to a reference. Its the exact same thing. Not sure if you are trying to just deceive me with some syntax sugar in C++ or what.
April 01, 2019
On Monday, 1 April 2019 at 13:09:28 UTC, Dein wrote:
> On Monday, 1 April 2019 at 01:18:44 UTC, Walter Bright wrote:
>> On 3/31/2019 5:35 PM, Rubn wrote:
>>> you can literally use it everywhere else you can use a type.
>>
>> No, you can't. An array of refs won't compile, either.
>>
>>   void test(int& a[]); // error
>>
>> A C++ ref can only appear at the top of a type AST, which is unlike any other type. Which exactly matches the only place a storage class can be!
>
> Seriously? You should know as well as I do what that function actually translates to:
>
>    void test(int&* const a);
>
> So I ask you now, how do you get a pointer to a reference. Its the exact same thing. Not sure if you are trying to just deceive me with some syntax sugar in C++ or what.

Bad example, but the point stands:


----
// foo.cpp
int fun() {
    int& foo[5]; // doesn't compile
}
----

I'd never even thought of it until Walter mentioned it that one can't have an array of references. Huh.
April 01, 2019
On Monday, 1 April 2019 at 01:19:41 UTC, Manu wrote:
> On Sun, Mar 31, 2019 at 5:20 PM Walter Bright via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
>>
>> On 3/31/2019 3:48 PM, Manu wrote:
> <snip>
> I don't know how you've never found yourself static-if-ing on ref-ness
> of stuff in every bit of functional meta you've ever written. Ref
> creates an out-of-language (we have no language features to interact
> with 'storage class') suite of conditions that I frequently have to
> wrangle my way through.

One can inspect the ref-ness of function parameters:

---------------
void main() {
    fun(5);
    int i;
    fun(i);
}

void fun(T)(auto ref T value) {
    import std.stdio;
    writeln("T: ", T.stringof, "   ref? ", __traits(isRef, value));
}

---------------

Output:

T: int   ref? false
T: int   ref? true


Unfortunately for me, this is a thing though:

https://issues.dlang.org/show_bug.cgi?id=19507

*I* think that's a bug (which is why I filed it), but I'm not even sure.