www.digitalmars.com         C & C++   DMDScript  

digitalmars.dip.ideas - Allow struct constructors with all parameters optional

reply Ogi <ogion.art gmail.com> writes:
It is valid to overload a function with all parameters optional 
with a function that takes no parameters. The parameterless 
overload is called if no arguments are passed:
```D
import std.stdio;
void fun(int x = 0, int y = 0) {
     writeln(i"fun($(x), $(y))");
}
void fun() {
     writeln("fun()");
}

fun(y:42); // fun(0, 42)
fun(); // fun()
```

Same rules apply to class constructors:
```D
import std.stdio;
class C {
     this(int x = 0, int y = 0) {
          writeln(i"C($(x), $(y))");
     }
     this() {
         writeln("C()");
     }
}

auto c1 = new C(y:42); // C(0, 42)
auto c2 = new C(); // C()
```

D disallows parameterless constructors for structs, and there are 
good reasons for this. But constructors with all parameters 
optional are prohibited too. This restriction doesn’t make much 
sense, especially now when D supports named arguments. This 
should be valid:

```D
struct S {
     this(int x = 0, int y = 0) {
          writeln(i"S($(x), $(y))");
     }
}
auto s1 = S(y:42); // S(0, 42)
auto s2 = S(); // default initialization
```
Aug 27
next sibling parent reply IchorDev <zxinsworld gmail.com> writes:
On Tuesday, 27 August 2024 at 08:48:19 UTC, Ogi wrote:
 D disallows parameterless constructors for structs, and there 
 are good reasons for this. But constructors with all parameters 
 optional are prohibited too. This restriction doesn’t make much 
 sense
I can’t remember exactly what the reasons for that were, but I thought those reasons applied equally to any constructors that *could* be called with 0 parameters? Could you also elaborate about how named parameters affect this situation?
Aug 27
parent Ogi <ogion.art gmail.com> writes:
On Tuesday, 27 August 2024 at 22:53:45 UTC, IchorDev wrote:
 I can’t remember exactly what the reasons for that were, but I 
 thought those reasons applied equally to any constructors that 
 *could* be called with 0 parameters?
The reasons are that, AFAIK, language designers decided that `S s` and `auto s = S()` producing different results would be too confusing. Speaking of constructors that could be called with zero parameters. All kinds of variadic constructors are perfectly fine, despite the fact that regular variadic functions can be called with zero parameters: ```D struct A { this(Args...)(Args) { writeln("ctor"); } } struct B { this(...) { writeln("ctor"); } } struct C { this(int[]...) { writeln("ctor"); } } struct D { this(Object...) { writeln("ctor"); } } ``` All these structs are default-initialized with zero arguments and call their constructor with non-zero number of arguments. Exactly how I expect a constructor with all optional parameters to behave.
 Could you also elaborate about how named parameters affect this 
 situation?
Consider this function: ``` void fun(int a = 0, int b = 0, int c = 0) ``` Before introduction of named arguments it wasn’t possible to call `fun` by only passing `c`. You had to pass all preceding arguments as well: `fun(0, 0, 42)`. So even if you could define a struct constructor with all optional parameters, this wouldn’t make any difference. You always had to pass at least the first argument anyway. Nowadays, thanks to named arguments, you actually can call `fun` by only passing `c`: `fun(c:42)`. With this change struct constructors with all optional parameters became useful, but they are still prohibited.
Aug 28
prev sibling next sibling parent reply Lance Bachmeier <no spam.net> writes:
On Tuesday, 27 August 2024 at 08:48:19 UTC, Ogi wrote:

 D disallows parameterless constructors for structs, and there 
 are good reasons for this. But constructors with all parameters 
 optional are prohibited too. This restriction doesn’t make much 
 sense, especially now when D supports named arguments. This 
 should be valid:

 ```D
 struct S {
     this(int x = 0, int y = 0) {
          writeln(i"S($(x), $(y))");
     }
 }
 auto s1 = S(y:42); // S(0, 42)
 auto s2 = S(); // default initialization
 ```
I'll point out that you can already write a library that makes your code run. I'd like a way to do it directly without jumping through these hoops. foo.d: ``` import std; struct S { this(int x, int y = 0) { writeln(i"S($(x), $(y))"); } } ``` bar.d: ``` import foo; alias _S = foo.S; _S S() { return _S(0, 0); } ``` baz.d: ``` public import foo, bar; alias S = bar.S; ``` call.d: ``` import baz; void main() { auto s2 = S(); } ```
Aug 28
next sibling parent reply Ogi <ogion.art gmail.com> writes:
On Wednesday, 28 August 2024 at 15:26:47 UTC, Lance Bachmeier 
wrote:
This proposal is not about allowing default struct constructors. Under this proposal, `S()` would still be initialized with `S.init`.
Aug 29
parent reply Lance Bachmeier <no spam.net> writes:
On Thursday, 29 August 2024 at 09:37:53 UTC, Ogi wrote:
 On Wednesday, 28 August 2024 at 15:26:47 UTC, Lance Bachmeier 
 wrote:
This proposal is not about allowing default struct constructors. Under this proposal, `S()` would still be initialized with `S.init`.
I'm not sure I understand. This is what you wrote:
 D disallows parameterless constructors for structs, and there 
 are good reasons for this. But constructors with all parameters 
 optional are prohibited too. This restriction doesn’t make much 
 sense, especially now when D supports named arguments. This 
 should be valid:
My example shows that it *is* valid, but only if you jump through unnecessary hoops.
Aug 29
parent Lance Bachmeier <no spam.net> writes:
On Thursday, 29 August 2024 at 13:20:04 UTC, Lance Bachmeier 
wrote:

 My example shows that it *is* valid, but only if you jump 
 through unnecessary hoops.
I don't mean valid, I mean you can already write code that is equivalent to the caller.
Aug 29
prev sibling parent reply ryuukk_ <ryuukk.dev gmail.com> writes:
On Wednesday, 28 August 2024 at 15:26:47 UTC, Lance Bachmeier 
wrote:
 On Tuesday, 27 August 2024 at 08:48:19 UTC, Ogi wrote:

 D disallows parameterless constructors for structs, and there 
 are good reasons for this. But constructors with all 
 parameters optional are prohibited too. This restriction 
 doesn’t make much sense, especially now when D supports named 
 arguments. This should be valid:

 ```D
 struct S {
     this(int x = 0, int y = 0) {
          writeln(i"S($(x), $(y))");
     }
 }
 auto s1 = S(y:42); // S(0, 42)
 auto s2 = S(); // default initialization
 ```
I'll point out that you can already write a library that makes your code run. I'd like a way to do it directly without jumping through these hoops. foo.d: ``` import std; struct S { this(int x, int y = 0) { writeln(i"S($(x), $(y))"); } } ``` bar.d: ``` import foo; alias _S = foo.S; _S S() { return _S(0, 0); } ``` baz.d: ``` public import foo, bar; alias S = bar.S; ``` call.d: ``` import baz; void main() { auto s2 = S(); } ```
Are you seriously telling people to write a library to be able to do what C already provides for decades? This is why nobody takes D seriously, all that code just to initialize a struct Unbelievable, seriously
Aug 29
next sibling parent Ogi <ogion.art gmail.com> writes:
On Thursday, 29 August 2024 at 13:57:05 UTC, ryuukk_ wrote:
 Are you seriously telling people to write a library to be able 
 to do what C already provides for decades?
Real yakuza use `static opCall`. (I’m kidding. Don’t use `opCall`, kids)
Aug 29
prev sibling next sibling parent Lance Bachmeier <no spam.net> writes:
On Thursday, 29 August 2024 at 13:57:05 UTC, ryuukk_ wrote:

 Are you seriously telling people to write a library to be able 
 to do what C already provides for decades?

 This is why nobody takes D seriously, all that code just to 
 initialize a struct

 Unbelievable, seriously
I'm saying the opposite. An intermediate-level D programmer can use modules and aliases in a straightforward way to accomplish the same thing, even on a third-party library that someone else wrote and that you don't want to change. The restriction isn't actually a restriction but an annoyance. It should be possible to do this within the struct itself.
Aug 29
prev sibling parent reply IchorDev <zxinsworld gmail.com> writes:
On Thursday, 29 August 2024 at 13:57:05 UTC, ryuukk_ wrote:
 Are you seriously telling people to write a library to be able 
 to do what C already provides for decades?
Since when did C99 have struct constructors? You can have a factory function in both languages, and they are the preferred method for ‘default construction’. However, in C you can’t ` disable this()`, so you have to trust the user to get it right by calling your factory function instead of just creating an uninitialised struct variable.
Aug 29
parent ryuukk_ <ryuukk.dev gmail.com> writes:
On Thursday, 29 August 2024 at 20:29:22 UTC, IchorDev wrote:
 On Thursday, 29 August 2024 at 13:57:05 UTC, ryuukk_ wrote:
 Are you seriously telling people to write a library to be able 
 to do what C already provides for decades?
Since when did C99 have struct constructors? You can have a factory function in both languages, and they are the preferred method for ‘default construction’. However, in C you can’t ` disable this()`, so you have to trust the user to get it right by calling your factory function instead of just creating an uninitialised struct variable.
i didn't say a constructor, this whole thread is the result of D not having the foundation right Allow this: ```D my_fun( Data { c: 1 } ); ``` And you don't need all of that extra shit
 However, in C you can’t ` disable this()`, so you have to trust 
 the user to get it right by calling your factory function 
 instead of just creating an uninitialised struct variable.
lol, no, reject bloat, and stick to simplicity
Aug 29
prev sibling parent Quirin Schroll <qs.il.paperinik gmail.com> writes:
On Tuesday, 27 August 2024 at 08:48:19 UTC, Ogi wrote:
 ```D
 struct S {
     this(int x = 0, int y = 0) {
          writeln(i"S($(x), $(y))");
     }
 }
 auto s1 = S(y: 42); // S(0, 42)
 auto s2 = S(); // default initialization
 ```
I don’t know if I like it or hate it. In contrast to the other ones, the nullary overload is visually present, whereas here, it’s not. Not being able to use the constructor with one or both arguments explicitly because you just aren’t allowed to define it is annoying, 100%. Working around that is possible, though: ```d safe: import std.stdio; struct S { private enum Y { _ } this(int x, int y = 0) { writeln(i"S($(x), $(y))"); } this(Y = Y.init, int y) { this(0, y); } } void main() { auto s = [ S(42), S(x: 42), S(y: 42), S(1, 2), S() ]; // prints: S(42, 0), S(42, 0), S(0, 42), S(1, 2) // absent: S(0, 0) } ``` The `Y = Y.init` makes the overload viable only through named arguments, so that `S(y: 42)` is possible. I’d be definitely in favor of it if the proposal included defining and defaulting the nullary constructor, which would be required in this case. Something like that: ```d struct S { default this(); // new this(int x = 0, int y = 0) { writeln(i"S($(x), $(y))"); } } ``` The nullary constructor can be ` disable`d, so making it also special in it being able to be `default`ed doesn’t really make difference.
Sep 05