September 23

On Thursday, 19 September 2024 at 15:56:06 UTC, Richard (Rikki) Andrew Cattermole wrote:

>

Would be nice to have proper struct inheritance but there are a lot of things we have to consider.

What about slicing?

struct A {
    int x;
}

struct B : A {
    int y;
    this(int x, int y) {
        super(x);
        this.y = y;
    }
}

void main() {
    B b1 = B(1, 2), b2 = B(3, 4);
    A* p = &b2;
    *p = b1;
    writeln(b2); // B(1, 4)
}

If we want something better than alias this, we have to prohibit implicit partial assignment via pointer/reference. When it’s necessary, it would still be possible with *p = cast(A)b1;.

September 24
On 24/09/2024 2:52 AM, Ogion wrote:
> On Thursday, 19 September 2024 at 15:56:06 UTC, Richard (Rikki) Andrew Cattermole wrote:
>> >>
> 
> Would be nice to have proper struct inheritance but there are a lot of things we have to consider.
> 
> What about slicing?
> 
> ```D
> struct A {
>      int x;
> }
> 
> struct B : A {
>      int y;
>      this(int x, int y) {
>          super(x);
>          this.y = y;
>      }
> }
> 
> void main() {
>      B b1 = B(1, 2), b2 = B(3, 4);
>      A* p = &b2;
>      *p = b1;
>      writeln(b2); // B(1, 4)
> }
> ```
> 
> If we want something better than `alias this`, we have to prohibit implicit partial assignment via pointer/reference. When it’s necessary, it would still be possible with `*p = cast(A)b1;`.

It is probably better to completely disable casting up the hierarchy in ``@safe`` code. If you need that, use ``opCast``.

It is not like with classes, where its guaranteed to have an allocation containing the entire thing, with a vtable.

Only concern is when you've got a pointer to a struct, and you cast up. ``opCast!(A*)`` support is likely what we want I think.

I'm not sure how we'd do that one. Maybe an attribute?
September 23
On 9/23/2024 7:52 AM, Ogion via dip.ideas wrote:
> On Thursday, 19 September 2024 at 15:56:06 UTC, Richard (Rikki) Andrew Cattermole wrote:
>> >>
> 
> Would be nice to have proper struct inheritance but there are a lot of things we have to consider.

Careful with the term 'proper'.  It doesn't add clarity.  It does implicitly divide everything else into some vague other category of improper, but without actually helping describe any actual particulars.

December 03

On Thursday, 19 September 2024 at 15:56:06 UTC, Richard (Rikki) Andrew Cattermole wrote:

>

As an idea this has come up in Razvan's DConf 2024 talk.

  1. Support inheritance on struct, for other structs.
struct Parent {
    ...
}

struct Child : Parent {
    ...
}
  1. opDispatch function, may work in place of alias this when no parent exists.
struct Parent {
    T thing;
    ref T opDispatch(string:"")() {
        return this.thing;
    }
}

struct Child : Parent {
}

Child child;
T got = child;

As I recall, the main purpose of 'alias this' is to offer implicit conversion of types. There is also the ability to have a member function called (property-like?), which seems like a somewhat different purpose.

When I first saw this inheritance suggestion mentioned (possibly in a recording of one of the DConf sessions), it struck me that something like the 'struct embedding' together with its implicit conversion for member functions on the embedded type may provide a similar mechanism.

This is something which is offered by Go/Limbo/Alef/Ken-C, the latter being used as the Plan 9 C compiler.

As I recall in the latter it was used for something like:

struct Lock {
  ...
};

bool lockit(struct Lock *);

struct Foo {
   ...
   Lock;
   ...
};

void something(/* ... */)
{
    struct Foo *foo = something_returning_Foo(/* ... */);

    if (!lockit(foo) {
        /* some error handling */
    }

    ...
}

Where the use of lockit() above is then called with '&foo->Lock' as an implicit conversion.

Personally, I'd rather have a struct like (POD) thing, even with member functions, which does not allow for the possibility of classful behaviour. If classful behaviour is desired, then somehow adjust classes so that they can be easily used as non-reference types.

December 04
On 04/12/2024 8:15 AM, Derek Fawcus wrote:
> As I recall, the main purpose of 'alias this' is to offer implicit conversion of types.  There is also the ability to have a member function called (property-like?), which seems like a somewhat different purpose.

It is two functionalities yes.

Which is why I've included both the inheritance as well as the reparenting behavior. Otherwise it isn't a complete replacement.

> When I first saw this inheritance suggestion mentioned (possibly in a recording of one of the DConf sessions), it struck me that something like the 'struct embedding' together with its implicit conversion for member functions on the embedded type may provide a similar mechanism.
> 
> This is something which is offered by Go/Limbo/Alef/Ken-C, the latter being used as the Plan 9 C compiler.
> 
> As I recall in the latter it was used for something like:
> 
> ```C
> struct Lock {
>    ...
> };
> 
> bool lockit(struct Lock *);
> 
> struct Foo {
>     ...
>     Lock;
>     ...
> };
> 
> void something(/* ... */)
> {
>      struct Foo *foo = something_returning_Foo(/* ... */);
> 
>      if (!lockit(foo) {
>          /* some error handling */
>      }
> 
>      ...
> }
> ```
> 
> Where the use of lockit() above is then called with '&foo->Lock' as an implicit conversion.

As a syntax that would imply multiple of these are possible.

Classes in D do not support multiple parent classes, so I don't think adding it for structs is appropriate.

If you don't need multiple parents, then the existing syntax works fine.

```d
struct Parent {
}

struct Foo : Parent {
}
```

> Personally, I'd rather have a struct like (POD) thing, even with member functions, which does not allow for the possibility of classful behaviour.  If classful behaviour is desired, then somehow adjust classes so that they can be easily used as non-reference types.

The class behavior that I do not want here is vtables and casting. Both of these have very good reasons why they would not be appropriate.

So there would be differences between them with good reasons why you would want classes over structs still.

December 03

On Tuesday, 3 December 2024 at 19:15:39 UTC, Derek Fawcus wrote:

>

Personally, I'd rather have a struct like (POD) thing, even with member functions, which does not allow for the possibility of classful behaviour. If classful behaviour is desired, then somehow adjust classes so that they can be easily used as non-reference types.

Also as I mentioned elsewhere, I don't like the idea of adding such to the betterC subset.

If one wants classful behaviour in betterC, then I'd suggest somehow making classes work in betterC, and leaving structs without and possibility of classful stuff. That keeps more closely to the spirit of C, and without any of the Simula-67 like C-with-Classes stuff.

Even then, I'd rather not have any form of classful behaviour (i.e. struct inheritance) in betterC, but that is obviously just my personal preference.

December 04
On 04/12/2024 8:30 AM, Derek Fawcus wrote:
> If one wants classful behaviour in betterC, then I'd suggest somehow making classes work in betterC, and leaving structs without and possibility of classful stuff. That keeps more closely to the spirit of C, and without any of the Simula-67 like C-with-Classes stuff.

C++ classes work in -betterC.

Its not too difficult to implement down casting either.

https://github.com/Project-Sidero/basic_memory/blob/main/source/sidero/base/allocators/classes.d

I.e.

https://github.com/Project-Sidero/fileformats/blob/master/source/sidero/fileformats/errors.d

December 03
On Tuesday, 3 December 2024 at 19:25:08 UTC, Richard (Rikki) Andrew Cattermole wrote:
> On 04/12/2024 8:15 AM, Derek Fawcus wrote:
>> As I recall, the main purpose of 'alias this' is to offer implicit conversion of types.  There is also the ability to have a member function called (property-like?), which seems like a somewhat different purpose.
>
> It is two functionalities yes.
>
> Which is why I've included both the inheritance as well as the reparenting behavior. Otherwise it isn't a complete replacement.

> As a syntax that would imply multiple of these are possible.

Yes, but it is literally equivalent to explicitly embedded one struct in another, which is already possible.  So one could have:

```C
struct Foo;
struct Bar;

struct Wrapper {
    ...
    Foo;
    Bar;
    ...
};
```

Which is just sugar for this:

```C
struct Wrapper {
    ...
    struct Foo Foo;
    struct Bar Bar;
    ...
};
```

It is just that having the embedded form allows the implicit conversion to happen, as it is a simple unambiguous sugar for accessing the appropriate member.


> Classes in D do not support multiple parent classes, so I don't think adding it for structs is appropriate.

Except D also already allows the explicit form above, but not the sugar for the field reference.  This is not inheritance, so there are no issues with embedding multiple structs, no diamond pattern issues, etc.  (I believe the Go spec explains all of this.)

What this gives is the implicit field conversion which 'alias this' seems to offer some form of, but in a different syntactical form, and allowing multiple such conversions.

However it probably won't cover all implicit conversion uses, e.g. 'alias this' to an 'int' member, then assigning the struct to an int var.  The scheme I'm suggesting only really covers calling functions with argument conversion.

> If you don't need multiple parents, then the existing syntax works fine.
>
> ```d
> struct Parent {
> }
>
> struct Foo : Parent {
> }
> ```

Would your proposal allow:

```d
struct Grandad {
}

struct Dad : Grandad {
}

struct Child : Dad {
}
```

(I'm not sure if the scheme I mention does, I'd have to reread the specs; but I believe it doesn't).


December 04
On 04/12/2024 9:19 AM, Derek Fawcus wrote:
> On Tuesday, 3 December 2024 at 19:25:08 UTC, Richard (Rikki) Andrew Cattermole wrote:
>> On 04/12/2024 8:15 AM, Derek Fawcus wrote:
>>> As I recall, the main purpose of 'alias this' is to offer implicit conversion of types.  There is also the ability to have a member function called (property-like?), which seems like a somewhat different purpose.
>>
>> It is two functionalities yes.
>>
>> Which is why I've included both the inheritance as well as the reparenting behavior. Otherwise it isn't a complete replacement.
> 
>> As a syntax that would imply multiple of these are possible.
> 
> Yes, but it is literally equivalent to explicitly embedded one struct in another, which is already possible.  So one could have:
> 
> ```C
> struct Foo;
> struct Bar;
> 
> struct Wrapper {
>      ...
>      Foo;
>      Bar;
>      ...
> };
> ```
> 
> Which is just sugar for this:
> 
> ```C
> struct Wrapper {
>      ...
>      struct Foo Foo;
>      struct Bar Bar;
>      ...
> };
> ```
> 
> It is just that having the embedded form allows the implicit conversion to happen, as it is a simple unambiguous sugar for accessing the appropriate member.

I'm very wary of this, Walter is pretty against implicit conversions generally speaking. I'm still trying to get a confirmation about the member-of-operator out of Walter which I have implemented (missing like two features).

Given that the utility of this is highly restricted to a subset of compositional needs, and the fact that it relies upon implicit conversions, I do not expect that this would be accepted.

However, I am in no way arguing against composition, I agree that it is better than inheritance normally. What this would be replacing, is not where composition is most valuable for.

Just in case you are interested, this is the function you would need to modify to implement the matching.

https://github.com/dlang/dmd/blob/d8c0e79bfb9ddb0e50603ffafa4c3d5539934f48/compiler/src/dmd/typesem.d#L998

>> Classes in D do not support multiple parent classes, so I don't think adding it for structs is appropriate.
> 
> Except D also already allows the explicit form above, but not the sugar for the field reference.  This is not inheritance, so there are no issues with embedding multiple structs, no diamond pattern issues, etc.  (I believe the Go spec explains all of this.)

I got all that. There is are issues from my perspective surrounding its compositional nature.

> What this gives is the implicit field conversion which 'alias this' seems to offer some form of, but in a different syntactical form, and allowing multiple such conversions.
> 
> However it probably won't cover all implicit conversion uses, e.g. 'alias this' to an 'int' member, then assigning the struct to an int var.  The scheme I'm suggesting only really covers calling functions with argument conversion.

Right, its a tool for composition. Different set of problems.

>> If you don't need multiple parents, then the existing syntax works fine.
>>
>> ```d
>> struct Parent {
>> }
>>
>> struct Foo : Parent {
>> }
>> ```
> 
> Would your proposal allow:
> 
> ```d
> struct Grandad {
> }
> 
> struct Dad : Grandad {
> }
> 
> struct Child : Dad {
> }
> ```
> 
> (I'm not sure if the scheme I mention does, I'd have to reread the specs; but I believe it doesn't).

Yes.

December 03
On Tuesday, 3 December 2024 at 20:45:32 UTC, Richard (Rikki) Andrew Cattermole wrote:

>> Would your proposal allow:
>> 
>> ```d
>> struct Grandad {
>> }
>> 
>> struct Dad : Grandad {
>> }
>> 
>> struct Child : Dad {
>> }
>> ```
>> 
>> (I'm not sure if the scheme I mention does, I'd have to reread the specs; but I believe it doesn't).
>
> Yes.

OK.

I'd suggest that the member variable version of 'alias this' is also a form of embedding / composition.

It allows one to control the exact in-memory layout of the struct so defined. Whereas AFAICT, the inheritance suggestion does not.

Issues/Questions with inheritance suggestion:

1. What is the in memory layout?
   (Presumably the 'parent' elements simply appear first in memory).
2. Inability to control in memory layout.
3. What type is passed to a parent struct member function when called on the child.
   (Presumably it is the class behaviour of passing the child stuct, with 'isA' relations)
   (If not, then there are two different behaviours for inheritance - class vs struct)
4. What other parts of the class behaviour will apply?
   (i.e. will the class implicit mutex lock now be instantiated)
   (if not, then classes and structs using inheritance will have different threading behaviour)