June 18, 2019
On 6/18/2019 3:34 AM, Ethan wrote:
>> Have you considered using template mixins? Since you're using a base struct merely as a namespace for some functions, that should work.
> 
> The ABI compatibility point really needs to be reiterated here. If the end goal, especially with things like D++, is to drive adoption of D by ensuring C++ interop is simple then that's now going to result in a static analysis tool to look at any given C++ struct and work out whether it should or shouldn't be converted to a mixin template. And introduce the dissonance of a C++ type not actually being a type in D.

You can do this:

  mixin template BaseMembers() {
    void memberFunction() { ... }
  }

  struct Base {
    mixin BaseMembers!();
  }

  struct Derived {
    mixin BaseMembers!();  // walla! zero size for "inheritance"!
    Base b;            // when I want C++ 1 byte size
  }

It's not a thing of beauty, but nothing connected with C++ is :-)
June 19, 2019
On Wed, Jun 19, 2019 at 5:55 AM Walter Bright via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
>
> On 6/18/2019 5:17 AM, Manu wrote:
> > Anyway, shared is my #1 concern.
>
> The more I've read about and studied concurrent programming issues, I tend to agree with you. If not #1, it's near #1.

On the plus side, a working shared solution is only a couple of lines away. We don't need anything fancy, actually less fancy than how it is now; just remove read/write, and let us see what we can do with the library (a lot). If/when we run into limitations with regards to library tooling, we can see how it looks from there.
June 19, 2019
On 18.06.19 21:58, Walter Bright wrote:
> On 6/18/2019 3:34 AM, Ethan wrote:
>>> Have you considered using template mixins? Since you're using a base struct merely as a namespace for some functions, that should work.
>>
>> The ABI compatibility point really needs to be reiterated here. If the end goal, especially with things like D++, is to drive adoption of D by ensuring C++ interop is simple then that's now going to result in a static analysis tool to look at any given C++ struct and work out whether it should or shouldn't be converted to a mixin template. And introduce the dissonance of a C++ type not actually being a type in D.
> 
> You can do this:
> 
>    mixin template BaseMembers() {
>      void memberFunction() { ... }
>    }
> 
>    struct Base {
>      mixin BaseMembers!();
>    }
> 
>    struct Derived {
>      mixin BaseMembers!();  // walla! zero size for "inheritance"!
>      Base b;            // when I want C++ 1 byte size
>    }
> 
> It's not a thing of beauty, but nothing connected with C++ is :-)

This works if Base and Derived are defined in the same module. Otherwise this does not do the correct thing, because mixin templates are analyzed in the scope into which they are mixed.
June 18, 2019
On 6/18/2019 5:01 PM, Timon Gehr wrote:
> This works if Base and Derived are defined in the same module. Otherwise this does not do the correct thing, because mixin templates are analyzed in the scope into which they are mixed.

That is correct, but with some care you can make it work, even if the mixin template functions just forward to other functions which are evaluated in the BaseMembers' scope.

June 19, 2019
On Wed, Jun 19, 2019 at 10:45 AM Walter Bright via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
>
> On 6/18/2019 5:01 PM, Timon Gehr wrote:
> > This works if Base and Derived are defined in the same module. Otherwise this does not do the correct thing, because mixin templates are analyzed in the scope into which they are mixed.
>
> That is correct, but with some care you can make it work, even if the mixin template functions just forward to other functions which are evaluated in the BaseMembers' scope.

I already have workarounds. That's not what this thread is about.
June 19, 2019
On Tuesday, 18 June 2019 at 18:39:41 UTC, Olivier FAURE wrote:
> On Tuesday, 18 June 2019 at 12:17:36 UTC, Manu wrote:
>>
>> alias this *should* be a very niche feature, but instead we abuse it
>> in lieu of struct inheritance, and no way to perform implicit
>> conversion. In both cases it's a gross code smell, and I hate it.
>
> It might be because I write a lot less generic code than you do, but I really don't understand why you lend so much importance to implicit conversion.
>
> XavierAP's question is particularly relevant here:
>
>> What is the benefit of
>>
>> 	struct A { void fun(); }
>> 	struct B :A {}
>>
>> 	B b;
>> 	b.fun;
>>
>> compared to
>>
>> 	struct A {}
>> 	struct B { A a; }
>>
>> 	B b;
>> 	b.a.fun;
>>
>
> Like, seriously, I might be missing something, but I don't get it.
>
> Can you cite some use case where inheritance is convenient but composition isn't? Ideally an actual use case that came up in a project?

I'm not against struct inheritance... Even though I don't get or haven't looked into Manu's application, and I rather try to understand the pros and cons in general, my impression is that he knows what he's talking about and has likely considered plenty of implications elsewhere, but he's tired of discussing over again. :p

In very general terms there are potential risks when things happen implicitly. A new feature with such effects is likelier to have unintended consequences at least in corner cases. That's where my questions were coming from.

But my initial guess is that there might be nothing wrong with struct inheritance, at least if the value semantics are preserved. And I do agree with Manu that alias this should be a very niche feature, and we are using it for structs as clumsier, dirtier and more verbose way to get the regular (static) polymorphism that we'd better implement by inheritance if possible.

Walter himself has admitted (in the multiple alias this thread) that alias this can be equivalent to inheritance (and that's argument enough why multiple's bad).

Something else in these discussions, the current fashion in CS is that inheritance is bad and you should use composition instead every single time -- just like the past OOP fad said the opposite against C. I agree that too deep or extensive inheritance hierarchies are a bad pattern, but inheritance isn't bad in itself or in moderation.

And again this discussion gets mixed up with C++ people who design classes inheriting from the STL. Since it's probably a bad idea to extend a library following the opposite architecture patterns and design intent of the library, it's probably a bad idea to inherit from the STL (and the lack of virtual constructors doesn't help), but this get mixed up with the idea that inheriting itself would be always a bad idea for any architecture. And the fact that the STL was not made with an (even non virtual) inheritance architecture (*ahem* std::basic_string *ahem*) also makes inheritance less popular -- nowadays in comparison with composition, even though STL uses templates not composition.
June 21, 2019
On Sunday, 9 June 2019 at 08:05:34 UTC, Manu wrote:
> I am really really tired of this pattern:
>
> struct DerivedStruct
> {
>     static if (BaseStruct.tupleof.length > 0)
>         BaseStruct base;
>     else
>         ref inout(BaseStruct) base() inout { return
> *cast(inout(BaseStruct)*)&this; }
>     alias base this;
>
>     // derived members
>     //...
> }
>
> Imagine if we could just write:
>
> struct DerivedStruct : BaseStruct
> {
>     // derived members
>     //...
> }
>
> Just imagine!

That is a good idea (ahem… )but what about multiple derived classes ?
I think the composition approach could help us by the use of the delegator concept from Ruby.

With a such approach a struct can:
- own multiple base/child struct/component
- choose which method to forward to the base/child struct/component

So at the and a such feature could be a sugar syntax of template Proxy: https://dlang.org/library/std/typecons/proxy.html

Reference:
- ruby delegator: https://blog.lelonek.me/how-to-delegate-methods-in-ruby-a7a71b077d99
- ruby delagator all supported methods: https://rubyreferences.github.io/rubyref/stdlib/patterns/delegate.html

As Example

struct Person {
  …
  int get_age(){…}
}

struct Career{
  …
  int get_experience(){…}
}

struct Employee{
  delegate: get_age, to: _person
  delegate: get_experience, to: _career

  immutable Person _person;
  immutable Career _career;

  this( immutable Person person , immutable Career career ){
    _person = person;
  _career = career;
  }
}




June 22, 2019
On Friday, 21 June 2019 at 22:19:12 UTC, bioinfornatics wrote:
> struct Employee{
>   delegate: get_age, to: _person
>   delegate: get_experience, to: _career
>
>   immutable Person _person;
>   immutable Career _career;

Listing all methods isn't very helpful, if that is the suggestion. Then a more consistent syntax would be

struct Employee {
alias getage = _person.getage;
alias get_experience = _career.get_experience;


Yet, that does not solve the MI problems.  Some are:

1. What to do if you both superclasses has a length() method.

2. What do do if both superclasses has used the same method name for unrelated concepts?

If there is no overlap semantically then problem 1 and 2 can be resolved with a cast to the super class, so that you get the right method.

However, what if the subclass has to modify them to retain the correct semantics? Which is really what MI problems are about.

Then you need som way of overloading a function that calls a specific attribute of  a struct. So you need overloading on attributes, not only overloading on types, yet you also need a reference to the parent struct.

So you need to be able to do something like

int length(SubStruct.attributename* this) {
    this.parent
}


Ola.


June 22, 2019
On Saturday, 22 June 2019 at 06:29:43 UTC, Ola Fosheim Grøstad wrote:
> Then you need som way of overloading a function that calls a specific attribute of  a struct. So you need overloading on attributes, not only overloading on types, yet you also need a reference to the parent struct.
>
> So you need to be able to do something like
>
> int length(SubStruct.attributename* this) {
>     this.parent
> }

In other words, you need static virtual methods. Perhaps a better syntax would be something along the lines of:

struct SubClass {
  int _length;

  inherit  SuperClass1 {
     int length(){   this.super.length() + this.parent._length}
  }  attribute1;

  inherit SuperClass2 {
     int length(){   this.super.length() - this.parent._length}
  } attribute2;
}


It is difficult to find a syntax that isn't confusing. Probably better to do it with interfaces.

June 22, 2019
On Saturday, 22 June 2019 at 06:29:43 UTC, Ola Fosheim Grøstad wrote:
> On Friday, 21 June 2019 at 22:19:12 UTC, bioinfornatics wrote:
>> struct Employee{
>>   delegate: get_age, to: _person
>>   delegate: get_experience, to: _career
>>
>>   immutable Person _person;
>>   immutable Career _career;
>
> Listing all methods isn't very helpful, if that is the suggestion. Then a more consistent syntax would be
>
> struct Employee {
> alias getage = _person.getage;
> alias get_experience = _career.get_experience;
>
>
> Yet, that does not solve the MI problems.  Some are:
>
> 1. What to do if you both superclasses has a length() method.
>
> 2. What do do if both superclasses has used the same method name for unrelated concepts?
>
> If there is no overlap semantically then problem 1 and 2 can be resolved with a cast to the super class, so that you get the right method.
>
> However, what if the subclass has to modify them to retain the correct semantics? Which is really what MI problems are about.
>
> Then you need som way of overloading a function that calls a specific attribute of  a struct. So you need overloading on attributes, not only overloading on types, yet you also need a reference to the parent struct.
>
> So you need to be able to do something like
>
> int length(SubStruct.attributename* this) {
>     this.parent
> }
>
>
> Ola.
Yes you are right, previously I show one sample case, when you want to choose which method will be forwarded to the component.


If you need to forward all child methods from the base struct you can use the template proxy from dlang or alias this.

For MI case, as here is a composition:
(i)a compiler's warning could be raised if method shadow another one
(ii) a sugar syntax to "alias" to synonym method name

delegate: @Person.size, to: size
delegate: @Career.size, to: length

(iii) a sugar syntax to forward all
delegate: @Person

Is an example of syntax using @annotation is possible to