www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Any way to reproduce Dart style constructors?

reply JN <666total wp.pl> writes:
One of my favourite language features of Dart (other one being 
factory constructors) are auto-assign constructors, for example 
(writing it in pseudo-D):

class Person
{
   string name;
   int age;
   this(this.age, this.name);
}

would translate to

class Person
{
   string name;
   int age;
   this(int age, string name)
   {
     this.age = age;
     this.name = name;
   }
}


It saves a lot of typing in the long run when doing lots of OOP, 
is there a way to reproduce such behaviour using mixins? I was 
thinking of some interface like:

class Person
{
   string name;
   int age;
   mixin(AutoConstructor!(age, name));
}

but I don't know if that's even doable using mixins. Even cooler 
might be something like an annotation (feels a bit Lombok-like 
from Java):

 AutoConstructor
class Person
{
   string name;
   int age;
}

but I don't think it's doable in D right now.

I am not looking for code, I can try that myself, just asking if 
such things are possible?
May 25 2017
next sibling parent Nicholas Wilson <iamthewilsonator hotmail.com> writes:
On Thursday, 25 May 2017 at 08:34:54 UTC, JN wrote:
 One of my favourite language features of Dart (other one being 
 factory constructors) are auto-assign constructors, for example 
 (writing it in pseudo-D):

 class Person
 {
   string name;
   int age;
   this(this.age, this.name);
 }

 would translate to

 class Person
 {
   string name;
   int age;
   this(int age, string name)
   {
     this.age = age;
     this.name = name;
   }
 }


 It saves a lot of typing in the long run when doing lots of 
 OOP, is there a way to reproduce such behaviour using mixins? I 
 was thinking of some interface like:

 class Person
 {
   string name;
   int age;
   mixin(AutoConstructor!(age, name));
 }

 but I don't know if that's even doable using mixins. Even 
 cooler might be something like an annotation (feels a bit 
 Lombok-like from Java):

  AutoConstructor
 class Person
 {
   string name;
   int age;
 }

 but I don't think it's doable in D right now.

 I am not looking for code, I can try that myself, just asking 
 if such things are possible?
Not sure about classes (I don't use them much) but structs have an automatically defined constructor. It is most definitely possible to do that with a mixin template (I remember someone recently showing autogenerating properties for readonly private members, sorry don't have a link). The UDA approach won't work because they are there for reflection. You can generate code based on the presence (or absence) of a UDA but you can't synthesise new methods with that without forwarding from a wrapper.
May 25 2017
prev sibling next sibling parent Moritz Maxeiner <moritz ucworks.org> writes:
On Thursday, 25 May 2017 at 08:34:54 UTC, JN wrote:
 One of my favourite language features of Dart (other one being 
 factory constructors) are auto-assign constructors, for example 
 (writing it in pseudo-D):

 class Person
 {
   string name;
   int age;
   this(this.age, this.name);
 }

 would translate to

 class Person
 {
   string name;
   int age;
   this(int age, string name)
   {
     this.age = age;
     this.name = name;
   }
 }


 It saves a lot of typing in the long run when doing lots of 
 OOP, is there a way to reproduce such behaviour using mixins? I 
 was thinking of some interface like:

 class Person
 {
   string name;
   int age;
   mixin(AutoConstructor!(age, name));
 }
The syntax would be mixin(AutoConstructor!(Person, "age", "name")), as 1. the compiler will throw you a `need 'this' to access member AutoConstructor` if you try to pass the arguments the way you did in the above 2. to avoid having to do ugly things to then get back at the type of those arguments again you will want to pass the type of the class in.
May 25 2017
prev sibling parent reply ag0aep6g <anonymous example.com> writes:
On 05/25/2017 10:34 AM, JN wrote:
 class Person
 {
    string name;
    int age;
    mixin(AutoConstructor!(age, name));
 }
[...]
 I am not looking for code, I can try that myself, just asking if such 
 things are possible?
I know you're not asking for code, but without experimenting I wouldn't have known that this works. In the end it's surprisingly simple: ---- mixin template AutoConstructor(fields ...) { this(typeof(fields) args) { fields = args; } } class Person { string name; int age; mixin AutoConstructor!(age, name); } void main() { auto p = new Person(42, "Arthur"); assert(p.age == 42); assert(p.name == "Arthur"); } ----
May 25 2017
parent reply Moritz Maxeiner <moritz ucworks.org> writes:
On Thursday, 25 May 2017 at 10:42:00 UTC, ag0aep6g wrote:
 On 05/25/2017 10:34 AM, JN wrote:
 class Person
 {
    string name;
    int age;
    mixin(AutoConstructor!(age, name));
 }
[...]
 I am not looking for code, I can try that myself, just asking 
 if such things are possible?
I know you're not asking for code, but without experimenting I wouldn't have known that this works. In the end it's surprisingly simple: ---- mixin template AutoConstructor(fields ...) { this(typeof(fields) args) { fields = args; } } ----
Be aware, though, that constructors mixed in via a mixin template behave differently with regards to overloading[1]. [1] https://issues.dlang.org/show_bug.cgi?id=11500
May 25 2017
parent reply ag0aep6g <anonymous example.com> writes:
On 05/25/2017 12:52 PM, Moritz Maxeiner wrote:
 Be aware, though, that constructors mixed in via a mixin template behave 
 differently with regards to overloading[1].
 
 [1] https://issues.dlang.org/show_bug.cgi?id=11500
Of course it couldn't be that simple :( Adam's workaround (`alias __ctor = mixin_thing.__ctor;`) might be workable, though. If that makes the usage too verbose, then a string mixin is the way to go, I guess. It's immune to issue 11500, but AutoConstructor isn't as nice to look at: ---- static string AutoConstructor(fields ...)() { import std.meta: staticMap; import std.traits: fullyQualifiedName; import std.string: join; enum fqns = staticMap!(fullyQualifiedName, fields); auto fields_str = "std.meta.AliasSeq!(" ~ [fqns].join(", ") ~ ")"; return " static import std.meta; this(typeof(" ~ fields_str ~ ") args) { " ~ fields_str ~ " = args; } "; } class Person { string name; int age; mixin(AutoConstructor!(age, name)); this(float f) {} } void main() { auto p = new Person(42, "Arthur"); assert(p.age == 42); assert(p.name == "Arthur"); } ---- Weird: AutoConstructor needs to be static. Doesn't make sense to me. Looks like a compiler bug.
May 25 2017
next sibling parent Seb <seb wilzba.ch> writes:
On Thursday, 25 May 2017 at 11:31:43 UTC, ag0aep6g wrote:
 On 05/25/2017 12:52 PM, Moritz Maxeiner wrote:
 [...]
Of course it couldn't be that simple :( Adam's workaround (`alias __ctor = mixin_thing.__ctor;`) might be workable, though. [...]
FWIW if this is properly working (e.g. with 11500 fixed), it would make a lot of sense to me to add it to Phobos as this looks like a very useful piece (useful enough maybe even try a DIP to get it into D).
May 25 2017
prev sibling parent reply Moritz Maxeiner <moritz ucworks.org> writes:
On Thursday, 25 May 2017 at 11:31:43 UTC, ag0aep6g wrote:
 On 05/25/2017 12:52 PM, Moritz Maxeiner wrote:
 Be aware, though, that constructors mixed in via a mixin 
 template behave differently with regards to overloading[1].
 
 [1] https://issues.dlang.org/show_bug.cgi?id=11500
Of course it couldn't be that simple :( Adam's workaround (`alias __ctor = mixin_thing.__ctor;`) might be workable, though. If that makes the usage too verbose, then a string mixin is the way to go, I guess. It's immune to issue 11500, but AutoConstructor isn't as nice to look at: ---- static string AutoConstructor(fields ...)() { [...] } ---- Weird: AutoConstructor needs to be static. Doesn't make sense to me. Looks like a compiler bug.
Nice. If you look at my initial reply to OP you'll see that I mentioned that error and I worked around it differently (worse than you). Didn't know that static would fix that (and I don't understand why it does), but thanks for the info!
May 25 2017
parent reply Moritz Maxeiner <moritz ucworks.org> writes:
On Thursday, 25 May 2017 at 12:35:57 UTC, Moritz Maxeiner wrote:
 On Thursday, 25 May 2017 at 11:31:43 UTC, ag0aep6g wrote:
 On 05/25/2017 12:52 PM, Moritz Maxeiner wrote:
 Be aware, though, that constructors mixed in via a mixin 
 template behave differently with regards to overloading[1].
 
 [1] https://issues.dlang.org/show_bug.cgi?id=11500
Of course it couldn't be that simple :( Adam's workaround (`alias __ctor = mixin_thing.__ctor;`) might be workable, though. If that makes the usage too verbose, then a string mixin is the way to go, I guess. It's immune to issue 11500, but AutoConstructor isn't as nice to look at: ---- static string AutoConstructor(fields ...)() { [...] } ---- Weird: AutoConstructor needs to be static. Doesn't make sense to me. Looks like a compiler bug.
Nice. If you look at my initial reply to OP you'll see that I mentioned that error and I worked around it differently (worse than you). Didn't know that static would fix that (and I don't understand why it does), but thanks for the info!
After thinking about this a bit I think I know why it doesn't work without static and it's not a compiler bug. Since --- string AutoConstructor(fields ...)() {} --- is just syntax sugar for --- template AutoConstructor(fields ...) { string AutoConstructor() {} } --- instantiating the template AutoConstructor inside class Person gives you a non-static member function AutoConstructor of class Person, so obviously we need an instance of Person to call it on. Or make it a static member function.
May 25 2017
parent reply ag0aep6g <anonymous example.com> writes:
On 05/25/2017 03:13 PM, Moritz Maxeiner wrote:
 After thinking about this a bit I think I know why it doesn't work 
 without static and it's not a compiler bug. Since
 
 ---
 string AutoConstructor(fields ...)() {}
 ---
 
 is just syntax sugar for
 ---
 template AutoConstructor(fields ...)
 {
      string AutoConstructor() {}
 }
 ---
 
 instantiating the template AutoConstructor inside class Person gives you 
 a non-static member function AutoConstructor of class Person, so 
 obviously we need an instance of Person to call it on. Or make it a 
 static member function.
I don't think that's it. The function itself is not mixed into the class. It's called and the result is mixed in. I don't see how it makes sense if the compiler tries to turn the called function into a method.
May 25 2017
parent reply Moritz Maxeiner <moritz ucworks.org> writes:
On Thursday, 25 May 2017 at 13:53:01 UTC, ag0aep6g wrote:
 On 05/25/2017 03:13 PM, Moritz Maxeiner wrote:
 After thinking about this a bit I think I know why it doesn't 
 work without static and it's not a compiler bug. Since
 
 ---
 string AutoConstructor(fields ...)() {}
 ---
 
 is just syntax sugar for
 ---
 template AutoConstructor(fields ...)
 {
      string AutoConstructor() {}
 }
 ---
 
 instantiating the template AutoConstructor inside class Person 
 gives you a non-static member function AutoConstructor of 
 class Person, so obviously we need an instance of Person to 
 call it on. Or make it a static member function.
I don't think that's it. The function itself is not mixed into the class. It's called and the result is mixed in. I don't see how it makes sense if the compiler tries to turn the called function into a method.
Well, then I guess we need a compiler guy to clear this up, because from my point of view, the template is instantiated within the scope of the class (way before we reach the mixin), nesting the template's scope within the class' scope, which makes the function within that template's scope a member function of the class.
May 25 2017
parent reply ag0aep6g <anonymous example.com> writes:
On 05/25/2017 08:14 PM, Moritz Maxeiner wrote:
 Well, then I guess we need a compiler guy to clear this up, because from 
 my point of view, the template is instantiated within the scope of the 
 class (way before we reach the mixin), nesting the template's scope 
 within the class' scope, which makes the function within that template's 
 scope a member function of the class.
You get a very similar error when you instantiate outside: ---- class Person { int age; } string AutoConstructor(fields ...)() { return ""; } enum s = AutoConstructor!(Person.age); /* Error: need 'this' for 'AutoConstructor' of type 'string()' */ ---- So I don't think it has anything to do with having the instantiation inside the class. Passing a class member to a function template seems to be the trigger. But it's more complicated than that. The instantiation itself goes through. The error only occurs when you actually call the function. Also, simply instantiating a function template inside a class doesn't result in a method. If it did, the function/method should be able to access class members. But it can't: ---- int ft()() { return age; } /* Error: undefined identifier age */ class Person { int age = 42; alias method = ft!(); /* error instantiating */ } ----
May 25 2017
parent reply Moritz Maxeiner <moritz ucworks.org> writes:
On Thursday, 25 May 2017 at 19:09:06 UTC, ag0aep6g wrote:
 [...]
 
 Also, simply instantiating a function template inside a class 
 doesn't result in a method. If it did, the function/method 
 should be able to access class members. But it can't:

 ----
 int ft()() { return age; } /* Error: undefined identifier age */
 class Person
 {
     int age = 42;
     alias method = ft!(); /* error instantiating */
 }
 ----
Ok, you are right. Open a bug report?
May 25 2017
parent ag0aep6g <anonymous example.com> writes:
On 05/25/2017 09:15 PM, Moritz Maxeiner wrote:
 Ok, you are right. Open a bug report?
Sure. https://issues.dlang.org/show_bug.cgi?id=17435
May 25 2017