September 29, 2014
On 28 September 2014 22:21, Andrei Alexandrescu via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
> On 9/27/14, 7:42 PM, Manu via Digitalmars-d wrote:
>>
>> void f() pure nothrow @nogc
>> {
>>     void localFunc()
>>     {
>>     }
>>
>>     localFunc();
>> }
>>
>> Complains because localFunc is not @nogc or nothrow. Doesn't complain about pure though.
>>
>> Is it reasonable to say that the scope of the outer function is nothrow+@nogc, and therefore everything declared within should also be so?
>
>
> Interesting. I'd guess probably not, e.g. a function may define a static local function and return its address (without either throwing or creating garbage), whereas that local function itself may do whatever it pleases.
>
> However, local functions have their body available by definition so they should have all deducible attributes deducted. That should take care of the problem.
>
>
> Andrei
>
> P.S. I also notice that my latest attempt at establishing communication has remained ignored.

I was out of town (was on my phone), and now I'm home with 2 guests, and we're working together. I can't sit and craft a pile of example cases until I'm alone and have time to do so. I haven't ignored it, but I need to find the time to give you what you want.

That said, my friend encountered one of my frequently recurring pain
cases himself yesterday:
struct S(T...)
{
  void f(T args) {}
}

S!(int, ref S) fail; // <-- no clean way to do this. I need this very frequently, and he reached for it too, so I can't be that weird.
September 29, 2014
On 9/28/2014 5:38 PM, Manu via Digitalmars-d wrote:
> That said, my friend encountered one of my frequently recurring pain
> cases himself yesterday:
> struct S(T...)
> {
>    void f(T args) {}
> }
>
> S!(int, ref S) fail; // <-- no clean way to do this. I need this very
> frequently, and he reached for it too, so I can't be that weird.

  S!(int, S*)

September 29, 2014
On 29 September 2014 10:51, Walter Bright via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
> On 9/28/2014 5:38 PM, Manu via Digitalmars-d wrote:
>>
>> That said, my friend encountered one of my frequently recurring pain
>> cases himself yesterday:
>> struct S(T...)
>> {
>>    void f(T args) {}
>> }
>>
>> S!(int, ref S) fail; // <-- no clean way to do this. I need this very frequently, and he reached for it too, so I can't be that weird.
>
>
>   S!(int, S*)

That's different.

I feel like I have to somehow justify to you guys how meta code works
in D. I have meta code that is no less than 5 layers deep. It's
complex, but at the same time, somehow surprisingly elegant and simple
(this is the nature of D I guess).
If I now assume throughout my meta "pointer means ref", then when I
actually pass a pointer in, the meta can't know if it was meant to be
a ref or not. It results in complex explicit logic to handle at almost
every point due to a loss of information.

You can't call f() with the same syntax anymore (you need an '&') which is a static if in the meta, you can't use the S* arg in the same meta (needs a '*') which is another static if. Assignments are changed, and unexpected indexing mechanics appear. When implementation logic expects and understands the distinction between pointers and ref's, this confuses that logic. When I interface between languages (everything I never do binds to at least C++, and in this case, also Lua), this complicates the situation.

I can't conflate 2 things that aren't the same. It leads to a lot of mess in a lot of places.
September 29, 2014
On 9/28/2014 6:31 PM, Manu via Digitalmars-d wrote:
>>    S!(int, S*)
> That's different.
>
> I feel like I have to somehow justify to you guys how meta code works
> in D. I have meta code that is no less than 5 layers deep. It's
> complex, but at the same time, somehow surprisingly elegant and simple
> (this is the nature of D I guess).
> If I now assume throughout my meta "pointer means ref", then when I
> actually pass a pointer in, the meta can't know if it was meant to be
> a ref or not. It results in complex explicit logic to handle at almost
> every point due to a loss of information.
>
> You can't call f() with the same syntax anymore (you need an '&')
> which is a static if in the meta, you can't use the S* arg in the same
> meta (needs a '*') which is another static if. Assignments are
> changed, and unexpected indexing mechanics appear. When implementation
> logic expects and understands the distinction between pointers and
> ref's, this confuses that logic. When I interface between languages
> (everything I never do binds to at least C++, and in this case, also
> Lua), this complicates the situation.
>
> I can't conflate 2 things that aren't the same. It leads to a lot of
> mess in a lot of places.

You're right that tuples in D cannot contain storage classes (and ref is just one storage class, there's also out and in, etc.).

You can use autoref, but I haven't understood why that doesn't work for you.

September 29, 2014
On 09/29/2014 04:43 AM, Walter Bright wrote:
>
> You're right that tuples in D cannot contain storage classes

void foo(ref int x){}
alias p=ParameterTypeTuple!foo;
pragma(msg, p); // (ref int)

(But this does not help.)
September 29, 2014
On 9/28/2014 9:23 PM, Timon Gehr wrote:
> On 09/29/2014 04:43 AM, Walter Bright wrote:
>>
>> You're right that tuples in D cannot contain storage classes
>
> void foo(ref int x){}
> alias p=ParameterTypeTuple!foo;
> pragma(msg, p); // (ref int)
>
> (But this does not help.)

You're right, I had forgotten about that.
September 29, 2014
On 9/28/14, 5:38 PM, Manu via Digitalmars-d wrote:
> I was out of town (was on my phone), and now I'm home with 2 guests,
> and we're working together. I can't sit and craft a pile of example
> cases until I'm alone and have time to do so. I haven't ignored it,
> but I need to find the time to give you what you want.

Thanks, don't feel under any obligation express or implied to follow through.

> That said, my friend encountered one of my frequently recurring pain
> cases himself yesterday:
> struct S(T...)
> {
>    void f(T args) {}
> }
>
> S!(int, ref S) fail; // <-- no clean way to do this. I need this very
> frequently, and he reached for it too, so I can't be that weird.

I understand. The short answer to this is D cannot do that and we cannot afford to change the language to make it do that.

There are two longer answers.

The first longer answer is there are ways to do that if it's a necessity, as you know and probably did. I agree it's not clean/easy.

The second longer answer is if you do this it means you're trying to write C++ in D: in C++ ref is part of the type (well... sort of) and therefore someone coming from C++ and translating the respective designs into D will find the change frustrating.

I've seen this pattern several times. Most recent was in conjunction with iterators vs. ranges. There's discussion going on in C++ circles about adding ranges to C++. One common issue raised by people gravitates around designs where the choice of using iterators is long foregone, and would think that replacing iterators with ranges throughout should just work. Take a look: http://article.gmane.org/gmane.comp.lib.boost.devel/191978

The reality is algorithms implemented with ranges look different from algorithms implemented with iterators. Similarly, generic code with D will look different from generic code with C++. Our hope is, of course, that all told range-based and D-generics code has advantages going for it, but the most dramatic of those advantages most certainly won't be discovered and reaped in designs that are copies of the respective C++ ones.

So you can't be that weird and neither is your friend, but I speculate that both of you have a solid C++ background :o).

About this situation in particular, yes, ref being part of the type does help this declaration, but overall as a design decision for the C++ programming value its value is questionable. T& is not a first class type and that hurts everyone everywhere - you can't create a value of that types, and virtually all parts of the language have special rules telling what happens when the actual type is a reference type. I think Walter made the right call here and we shouldn't change anything.


Andrei

September 29, 2014
On 29 September 2014 12:43, Walter Bright via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
> On 9/28/2014 6:31 PM, Manu via Digitalmars-d wrote:
>>>
>>>    S!(int, S*)
>>
>> That's different.
>>
>> I feel like I have to somehow justify to you guys how meta code works
>> in D. I have meta code that is no less than 5 layers deep. It's
>> complex, but at the same time, somehow surprisingly elegant and simple
>> (this is the nature of D I guess).
>> If I now assume throughout my meta "pointer means ref", then when I
>> actually pass a pointer in, the meta can't know if it was meant to be
>> a ref or not. It results in complex explicit logic to handle at almost
>> every point due to a loss of information.
>>
>> You can't call f() with the same syntax anymore (you need an '&') which is a static if in the meta, you can't use the S* arg in the same meta (needs a '*') which is another static if. Assignments are changed, and unexpected indexing mechanics appear. When implementation logic expects and understands the distinction between pointers and ref's, this confuses that logic. When I interface between languages (everything I never do binds to at least C++, and in this case, also Lua), this complicates the situation.
>>
>> I can't conflate 2 things that aren't the same. It leads to a lot of mess in a lot of places.
>
>
> You're right that tuples in D cannot contain storage classes (and ref is just one storage class, there's also out and in, etc.).

I know, that's my whole point. I think 'storage class' is the original sin.
in is declared as scope const, or in my fantasy world scope(const(T)).

out is the only interesting storage class (conceptually) I've used. It is effectively just an alias for ref, but it also encodes some special non-type information, which is to initialise prior to the call.


> You can use autoref, but I haven't understood why that doesn't work for you.

I'm not writing java or python code here. I consider it a fundamental
quality of a 'systems language', or perhaps just a native language,
that I have the explicit power to produce binary I intend.
I can't have the compiler deciding if something is to be ref or not.
My code is rarely only consumed by other D code. I have lots of cross
language linkage, and also dynamic libraries (which expect a specific
ABI).

auto ref wouldn't work in that situation I gave above anyway...
S!(auto ref T) is no more valid than S!(ref T).
September 29, 2014
On Monday, 29 September 2014 at 08:02:45 UTC, Andrei Alexandrescu
wrote:
> I understand. The short answer to this is D cannot do that and we cannot afford to change the language to make it do that.
>
> There are two longer answers.
>

I think this is because ref have several conflated meaning:
  - "I want to mess up with the argument" (à la swap). This is the
meaning it has right now.
  - "Burrowing". Which is the same as previous behavior, except
for classes and delegates, so the whole scope story, as they are
natural "reference types".
  - "Do not copy" aka const ref. This one is for performance
reason. It doesn't really matter if a copy is made or not as the
damn thing is const, but one want to avoid expensive copy when
the thing passed down is fat.

Each of them have their own set of cases where you want and do
not want ref.
September 30, 2014
On 29 September 2014 18:02, Andrei Alexandrescu via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
> On 9/28/14, 5:38 PM, Manu via Digitalmars-d wrote:
>>
>> I was out of town (was on my phone), and now I'm home with 2 guests, and we're working together. I can't sit and craft a pile of example cases until I'm alone and have time to do so. I haven't ignored it, but I need to find the time to give you what you want.
>
>
> Thanks, don't feel under any obligation express or implied to follow through.

Well, after your unflattering caricature, I intend to take the time to
try and craft some good examples of various cases.
I also have a strong suspicion (valid or otherwise) that there is
literally nothing I could do to convince you, no matter the quality of
my evidence.
There will always be the "it's too late", "it's a breaking change!",
defence. Unless you and Walter are both invested in a change, I'm
pretty certain it's impossible.
So, your observation may well have been fair. Perhaps that is a true
pattern of mine, but I don't think 'I'm just a dick like that', I
think there is reason behind it.

There is certainly time in the past where I've gone to great lengths
to try and prove my cases. In more recent times, and as my (few)
outstanding daily problems with the language have become more
controversial, I've become fairly convinced that such effort on my
part is just wasting my time, and only serves as a process for me to
highlight my frustrations to myself.
You obviously don't approach programming the same way I do, I can't
appeal to you.

Upon closer examination of my mood and feelings interacting with this
forum recently, I realise that this is a significant factor in my
interaction, although perhaps being mostly subconscious. Ie, I'm more
likely to disappear and try and get some work done, than spend the
time trying to win a futile argument.
I did try very hard to extract myself from this NG, but it seems it's
very hard to resist! When topics I care about appear, or when I'm just
wildly frustrated about something that agitates me on a daily basis, I
keep coming back! >_<

Point is, I feel like I've been an engaged member of this community
for a long time now, but I feel like I have practically nothing more
to add.
My experience and industry use cases are no longer interesting,
they've presented any value that they had already. I get the feeling
I've affected all of the change that I am capable of, and I need to
decide if I'm happy with D as is, or not, rather than maintain my
ambient frustration that it's sitting at 99%, with the last 1%
unattainable to me unless I fork the language for my own use :/

Trouble for me is, I've invested so much time now. I find myself in a very awkward situation where I'm too far in... I can't go back to C++, but the situation is such that I'll never convince the rest of my colleagues to jump on board. Believe it or not, I'm a lot more reasonable (and patient) than most.


I'm quite certain that these days, that thing that Scott Myers mentioned about the C++ committee; where practically anything can be used to justify anything, so getting changes approved is mostly an appeal to the emotional state of the people in charge, is already present in D.


>> That said, my friend encountered one of my frequently recurring pain
>> cases himself yesterday:
>> struct S(T...)
>> {
>>    void f(T args) {}
>> }
>>
>> S!(int, ref S) fail; // <-- no clean way to do this. I need this very frequently, and he reached for it too, so I can't be that weird.
>
>
> I understand. The short answer to this is D cannot do that and we cannot afford to change the language to make it do that.
>
> There are two longer answers.
>
> The first longer answer is there are ways to do that if it's a necessity, as you know and probably did. I agree it's not clean/easy.
>
> The second longer answer is if you do this it means you're trying to write C++ in D: in C++ ref is part of the type (well... sort of) and therefore someone coming from C++ and translating the respective designs into D will find the change frustrating.

What about someone *interacting* with C++, not just 'coming from'?
I also interact with Lua and C# a lot. Lua is ref by nature, C# also
uses ref a lot.
In 6 years, the only time I've found an opportunity to use D in
isolation, is a small webserver in vibe.d.

My opinion is that ref is agreed to be critically important, as demonstrated to me by all the other languages I use.


> I've seen this pattern several times. Most recent was in conjunction with iterators vs. ranges. There's discussion going on in C++ circles about adding ranges to C++. One common issue raised by people gravitates around designs where the choice of using iterators is long foregone, and would think that replacing iterators with ranges throughout should just work. Take a look: http://article.gmane.org/gmane.comp.lib.boost.devel/191978
>
> The reality is algorithms implemented with ranges look different from algorithms implemented with iterators. Similarly, generic code with D will look different from generic code with C++. Our hope is, of course, that all told range-based and D-generics code has advantages going for it, but the most dramatic of those advantages most certainly won't be discovered and reaped in designs that are copies of the respective C++ ones.

I'm not writing ranges or generics. Those things are fine, and they are also self-contained within D.

I obviously use D completely differently to you, and value an entirely
different set of features.
Trust me, I'm not copying designs from C++, I use D specifically for
use cases where C++ is utterly impotent.

For me, 80% of generic code is about binding things (and a further 15%
is serialisation). As said before, C++, C#, Lua; it is super-common in
gamedev for the ecosystem to be made of (at least) 3 significant
languages.
Bindings are traditionally cumbersome, brittle, and one of the biggest
hassles, frustrations, sources of time/productivity loss using C++ for
game tech, is maintaining such bindings.
D is the only language I know powerful enough to automate that mess.
This is why D was attractive to me, and it is still why D is
attractive to me. I _can't escape ref_ when writing this sort of code,
and I write a lot of it.
I don't think it's at all fair to say "I'm using D wrong", or "stop
trying to be a C++ user in D", that's not the case here..


It's also a bit strange to hear you say all this when the key focus of development at the moment is "C++, GC", "C++, GC".


> So you can't be that weird and neither is your friend, but I speculate that both of you have a solid C++ background :o).

No, I think we're pretty normal, and populace.


> About this situation in particular, yes, ref being part of the type does help this declaration, but overall as a design decision for the C++ programming value its value is questionable. T& is not a first class type and that hurts everyone everywhere - you can't create a value of that types, and virtually all parts of the language have special rules telling what happens when the actual type is a reference type.

You're saying T& is not a first-class type and hurts everyone everywhere, I agree, and D is no different, except it is even worse. I'd like to explore ref as a first-class type. Do you have reason to believe that's completely unworkable? I'm not suggesting to clone C++'s design. I think there's room for a design that improves on C++.


> I think Walter made the right call here and we shouldn't change anything.

This pretty much validates my opening suspicion. What do I do? STFU and deal with it?

I suspect you'd think very differently if you worked with the people I
do, have the conversations with colleagues I have, and struggled with
the code I have for as long as I have. I dread to think how much ref
has cost me in $/hr terms.
You make ref sound worthless. Is there actually any situation where
you find value in ref?
If it's meaningless to you, and so 'un-D', why not let it have useful
meaning for those who find it significant, important even? :/