August 25

On Sunday, 25 August 2024 at 05:07:43 UTC, Manu wrote:

>

This is a really old drum to beat on... but maybe we need to start pounding
it again.

It's not strictly this simple though; I think there's a lot of work up-front. We don't really even know what a tuple is, or even what "kinds" of things the language can express...

Like, there's more than types and values. There's symbol aliases, which may or may not carry an instance reference with them, declaration aliases, which may carry names and properties like storage class, there's sometimes expression aliases, there's potentially attributes, and heaps of other stuff that exists outside the language; like storage class, which doesn't seem to have any presence in the language at all; it's queried with a __traits and comically tells you those facts as string literals! I think this space might be the heart and soul of D's foundational issues, and as such, it's excruciatingly hard to make corrections.

Like, from my example above, what even IS s.tupleof? It's some kind of
list of what kind of thing? Direct symbol references to members of a live
instance?
If s.tupleof can populate a list with that kind of thing, why doesn't this
work:

struct S
{
int x, y;
}

struct T
{
S s;
int i;

alias a = i; // works
alias b = s.x; // Doesn't work? Why?
}

All good points I think. AliasSeq and aliases in general have always been a sore spot in D meta.

>

It seems like s.tupleof presents that there are semantics in the language to express this sort of thing, but it's not clear what to call that.

Likewise in my example above:

alias a = s.tupleof; // this can hold the list of references, which seem to
carry around their instance reference with them
alias b = s.tupleof[0]; // this emits a surprising error message:

error : alias b cannot alias an expression AliasSeq!(s.x, s.y)[0]

So, that 'list' I mention; does this error message imply that this list given by tupleof is an AliasSeq? What exactly IS an AliasSeq? What is the actual set of things that it can hold?

If tupleof is an AliasSeq, then what's going on here:

alias c = AliasSeq!(s.tupleof);
alias d = c[0]; // the error is now gone??

At first, I was thinking WTF is this? How can this work?

But the answer is, it doesn't.

if you try to use d you will get an error, because there is no instance -- you ended up aliasing the symbol without the instance.

I almost feel like we need a distinction between alias-with-instance and alias-of-symbol.

There are more WTFs, like in certain cases aliases will carry along names along with the types, and in some cases they don't.

I remember classically this pattern (before static foreach):

foreach(i, _unused; x.tupleof)
{
    // use x.tupleof[i] here instead of _unused, because the former gives names
}
>

I think a good indication that this whole space is profoundly troubled when classic patterns like this emerge:

template T(alias X) { use X } // oh no you don't, alias is very
opinionated! you'll need this instead:
template T(X...) if (X.length == 1) { use X[0] }

This is no longer needed, so at least there is a bit of progress.

>

No matter how you look at it though, this shouldn't be a valid forum post in 2024...

Agreed.

-Steve

August 25

On Sunday, 25 August 2024 at 16:26:47 UTC, Steven Schveighoffer wrote:

>

AliasSeq and aliases in general have always been a sore spot in D meta.

Hard no, overload sets and auto flattening are super magic and just great. The spec and the book should just have explanations about the behavior

August 26
On Mon, 26 Aug 2024 at 02:31, Steven Schveighoffer via Digitalmars-d < digitalmars-d@puremagic.com> wrote:

> On Sunday, 25 August 2024 at 05:07:43 UTC, Manu wrote:
> > This is a really old drum to beat on... but maybe we need to
> > start pounding
> > it again.
> >
> > It's not strictly this simple though; I think there's a lot of work up-front. We don't really even know what a tuple is, or even what "kinds" of things the language can express...
> >
> > Like, there's more than types and values. There's symbol aliases, which may or may not carry an instance reference with them, declaration aliases, which may carry names and properties like storage class, there's sometimes expression aliases, there's potentially attributes, and heaps of other stuff that exists outside the language; like storage class, which doesn't seem to have any presence in the language at all; it's queried with a __traits and comically tells you those facts as string literals! I think this space might be the heart and soul of D's foundational issues, and as such, it's excruciatingly hard to make corrections.
> >
> > Like, from my example above, what even IS `s.tupleof`? It's
> > some kind of
> > list of what kind of thing? Direct symbol references to members
> > of a live
> > instance?
> > If s.tupleof can populate a list with that kind of thing, why
> > doesn't this
> > work:
> >
> > struct S
> > {
> >   int x, y;
> > }
> >
> > struct T
> > {
> >   S s;
> >   int i;
> >
> >   alias a = i; // works
> >   alias b = s.x; // Doesn't work? Why?
> > }
>
> All good points I think. AliasSeq and aliases in general have always been a sore spot in D meta.
>

Indeed, and despite that, it's also quite likely that it is simultaneously
also D's single most important and noteworthy feature! Above literally
everything else by a country mile.
I think every meaningful advantage that D brings to the table essentially
stems from `alias` in some form, or this general realm of D's meta.

I think it's really important to try and make a strong case that this stuff is of the most critical importance. There's fault lines and fractures all over the place; this category of issues should be addressed with a laser focus and ruthless murder... and it should have happened 15 years ago.


> It seems like s.tupleof presents that there are semantics in
> > the language to express this sort of thing, but it's not clear what to call that.
> >
> > Likewise in my example above:
> >
> > alias a = s.tupleof; // this can hold the list of references,
> > which seem to
> > carry around their instance reference with them
> > alias b = s.tupleof[0]; // this emits a surprising error
> > message:
> >
> > error : alias `b` cannot alias an expression `AliasSeq!(s.x,
> > s.y)[0]`
> >
> > So, that 'list' I mention; does this error message imply that this list given by `tupleof` is an AliasSeq? What exactly IS an AliasSeq? What is the actual set of things that it can hold?
> >
> > If tupleof is an AliasSeq, then what's going on here:
> >
> > alias c = AliasSeq!(s.tupleof);
> > alias d = c[0]; // the error is now gone??
>
> At first, I was thinking WTF is this? How can this work?
>
> But the answer is, it doesn't.
>
> if you try to *use* `d` you will get an error, because there is no instance -- you ended up aliasing the symbol without the instance.
>

Oh nice catch...
But that just leads to the question; what's with `b` then?
Why did `b` emit the error eagerly at the assignment, but `d` decided to
take a lazy approach as you say?

What I really want to know though; why do these aliases drop the instance?
That's just a bug right?
There's no reason you would want that behaviour, and it's obvious that an
alias+instance reference *can* be carried by the AST, because it's there
before it's unceremoniously dropped basically whenever you touch it.


> I think a good indication that this whole space is profoundly
> > troubled when classic patterns like this emerge:
> >
> > template T(alias X) { use X }   // oh no you don't, alias is
> > very
> > opinionated! you'll need this instead:
> > template T(X...) if (X.length == 1) { use X[0] }
>
> This is no longer needed, so at least there is a bit of progress.
>

It's not?
I just had to write an instance of this hack yesterday, for the same
traditional reasons as ever... :/
What changed? I'll take another look at it.


August 25
On Sunday, 25 August 2024 at 17:27:33 UTC, Manu wrote:
> 
> But that just leads to the question; what's with `b` then?

I believe its a struct offset

I dont understand the usecase, but it goes into traits
```
struct S{
	int x,y;
}
struct S_{
	int y,x;
}
unittest{
	import std;
	S s;
	alias b = s.x;
	S_ s_;
	__traits(child, s, b)=1;//works
	//__traits(child, s_, b)=1;//errors, when S_.y is also an int in the same place and a S_.x exists *grumble*
}
```
> Why did `b` emit the error eagerly at the assignment, but `d` decided to
> take a lazy approach as you say?

aliases can be lazy if they reference a lazy compilation error

```d
alias Seq(T...)=T;
template foo(int i){
	void foo()(){
		static if(i==1){
			static assert(0);
		}
		import std;
		i.writeln;
	}
}
template bar(int i){
	alias foobar=Seq!(foo!i);
	alias bar=foobar[0];
}
unittest{
	alias barfoo=bar!1;
	bar!2;
}
```
August 25
On Sunday, 25 August 2024 at 17:27:33 UTC, Manu wrote:
> Oh nice catch...
> But that just leads to the question; what's with `b` then?
> Why did `b` emit the error eagerly at the assignment, but `d` decided to
> take a lazy approach as you say?
>
> What I really want to know though; why do these aliases drop the instance?
> That's just a bug right?
> There's no reason you would want that behaviour, and it's obvious that an
> alias+instance reference *can* be carried by the AST, because it's there
> before it's unceremoniously dropped basically whenever you touch it.

Just in case, the kind of alias you are debating about is to be deprecated, see https://dlang.org/spec/legacy.html#alias-instance-member.

It seems that what you are looking for is more a kind of system of parameter-less expression-macros. I have put a link to an old PR in a previous answer. You'll see that the main issue is that such aliases needs to be recontextualized for each new use.
August 25
On 8/25/24 19:27, Manu wrote:
> There's fault lines and fractures all over the place; this category of issues should be addressed with a laser focus and ruthless murder... and it should have happened 15 years ago.

FWIW with my frontend this just works:

```d
struct S{ int x; }
void foo(alias a)(){ a=2; }
int bar(){
    S s;
    foo!(s.x)();
    return s.x;
}
static assert(bar()==2);
```


August 25
On 8/25/24 21:35, user1234 wrote:
> 
> Just in case, the kind of alias you are debating about is to be deprecated, see https://dlang.org/spec/legacy.html#alias-instance-member.

Well, first it should be deprecated, then at some point it should just be made to work the way people would expect.
August 26
On Mon, 26 Aug 2024, 05:46 Timon Gehr via Digitalmars-d, < digitalmars-d@puremagic.com> wrote:

> On 8/25/24 19:27, Manu wrote:
> > There's fault lines and fractures all over the place; this category of issues should be addressed with a laser focus and ruthless murder... and it should have happened 15 years ago.
>
> FWIW with my frontend this just works:
>
> ```d
> struct S{ int x; }
> void foo(alias a)(){ a=2; }
> int bar(){
>      S s;
>      foo!(s.x)();
>      return s.x;
> }
> static assert(bar()==2);
> ```
>

Strap your fork to a CI machine and I'll use it exclusively! :)

Or, you know, make a PR.

>


August 25
On 8/24/2024 11:59 PM, Manu wrote:
> Here's another one just now:
> 
> return __traits(getMember, instance, funName)(args);   // this works as it should
> 
> 
> alias fun = __traits(getMember, instance, funName);
> return fun(args);   // error : calling non-static function `myFun` requires an instance of type `T`
> 
> 
> Again... this isn't acceptable in 2024. No 'explanation' should be acceptable to any sound mind. It's a critical bug, and it should have been fixed 15-20 years ago.

Thank you for posting this, but it is an incomplete example. Please don't make me guess what is missing - it's a big time saver if you provide it.

August 25
On 8/24/2024 9:36 AM, Manu wrote:
> alias x = s.tupleof; // this works
> alias y = s.tupleof[0]; // this is a compile error?!


Can you please repost with the missing declarations from your example? Thanks!