www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Named constructors

reply Dru <dru notreal.com> writes:
Another way to distinguish between constructors is needed.
Because it is possible to have two different constructors that 
take the same arguments.
Adding dummy arguments that are unused hurts code clarity.
Jan 08 2019
next sibling parent reply Alex <sascha.orlov gmail.com> writes:
On Wednesday, 9 January 2019 at 07:47:02 UTC, Dru wrote:
 Another way to distinguish between constructors is needed.
 Because it is possible to have two different constructors that 
 take the same arguments.
 Adding dummy arguments that are unused hurts code clarity.
Couldn't this problem be solved by a factory method? I mean, either this, or something like this: http://www.cs.technion.ac.il/users/yechiel/c++-faq/named-ctor-idiom.html ´´´ import std.stdio; import std.math; void main() { auto pr = Point.rectangular(2,3); assert(approxEqual(pr.x_, 2)); assert(approxEqual(pr.y_, 3)); auto pp = Point.polar(1,PI); assert(approxEqual(pp.x_, -1)); assert(approxEqual(pp.y_, 0)); } struct Point { public: static Point rectangular(real x, real y) // Rectangular coord's { return Point(x,y); } static Point polar(real radius, real angle) // Polar coordinates { return Point(radius * cos(angle), radius * sin(angle)); } private: this(real x, real y) { x_ = x; y_ = y; } real x_, y_; } ´´´
Jan 09 2019
next sibling parent reply bauss <jj_1337 live.dk> writes:
On Wednesday, 9 January 2019 at 08:53:40 UTC, Alex wrote:
 On Wednesday, 9 January 2019 at 07:47:02 UTC, Dru wrote:
 Another way to distinguish between constructors is needed.
 Because it is possible to have two different constructors that 
 take the same arguments.
 Adding dummy arguments that are unused hurts code clarity.
Couldn't this problem be solved by a factory method? I mean, either this, or something like this: http://www.cs.technion.ac.il/users/yechiel/c++-faq/named-ctor-idiom.html ´´´ import std.stdio; import std.math; void main() { auto pr = Point.rectangular(2,3); assert(approxEqual(pr.x_, 2)); assert(approxEqual(pr.y_, 3)); auto pp = Point.polar(1,PI); assert(approxEqual(pp.x_, -1)); assert(approxEqual(pp.y_, 0)); } struct Point { public: static Point rectangular(real x, real y) // Rectangular coord's { return Point(x,y); } static Point polar(real radius, real angle) // Polar coordinates { return Point(radius * cos(angle), radius * sin(angle)); } private: this(real x, real y) { x_ = x; y_ = y; } real x_, y_; } ´´´
I like this approach to it and you're entirely correct. It would be nice with support for this in the language without having to create factory methods. Basically something like the following could be lowered to your code. ´´´ import std.stdio; import std.math; void main() { auto pr = Point.rectangular(2,3); assert(approxEqual(pr.x_, 2)); assert(approxEqual(pr.y_, 3)); auto pp = Point.polar(1,PI); assert(approxEqual(pp.x_, -1)); assert(approxEqual(pp.y_, 0)); } struct Point { public: this(real x, real y) { this.x = x; this.y = y; } this rectangular(real x, real y) { this(x,y); } this polar(real radius, real angle) { this(radius * cos(angle), radius * sin(angle)); } real x, y; } ´´´
Jan 09 2019
parent reply JN <666total wp.pl> writes:
On Wednesday, 9 January 2019 at 10:02:12 UTC, bauss wrote:
 I like this approach to it and you're entirely correct.

 It would be nice with support for this in the language without 
 having to create factory methods.

 Basically something like the following could be lowered to your 
 code.
I was going to say that this post will be mostly ignored and people will present some template magic to partially implement such feature (just add few imports and some boilerplate code), but I guess I got beaten to it :)
Jan 09 2019
next sibling parent reply Neia Neutuladh <neia ikeran.org> writes:
On Wed, 09 Jan 2019 20:54:48 +0000, JN wrote:
 I was going to say that this post will be mostly ignored and people will
 present some template magic to partially implement such feature (just
 add few imports and some boilerplate code),
 but I guess I got beaten to it :)
It's handy when writing the DIP to have as many workarounds as possible. Then you can show why they aren't good enough in advance and save a round of drafts.
Jan 09 2019
parent reply JN <666total wp.pl> writes:
On Wednesday, 9 January 2019 at 21:41:34 UTC, Neia Neutuladh 
wrote:
 On Wed, 09 Jan 2019 20:54:48 +0000, JN wrote:
 I was going to say that this post will be mostly ignored and 
 people will
 present some template magic to partially implement such 
 feature (just
 add few imports and some boilerplate code),
 but I guess I got beaten to it :)
It's handy when writing the DIP to have as many workarounds as possible. Then you can show why they aren't good enough in advance and save a round of drafts.
Perhaps, but sometimes it feels like the language features are set in stone at this point. As long as you can implement a language feature X with template magic, even if it's partially implementing X, it will be used as an excuse why X shouldn't be a language feature. I mean, I see the benefits of a lightweight language with powerful metaprogramming features, but I am also wary of it ending up with a Lisp Curse ( http://www.winestockwebdesign.com/Essays/Lisp_Curse.html - especially the part with "Imagine that a strong rivalry develops between Haskell and Common Lisp. What happens next?" ). Some language features are almost straight syntax sugar, and there's nothing wrong with that either. Consider foreach in D. I am sure it could be implemented with a few mixins, and yet, it is a language feature.
Jan 09 2019
parent Neia Neutuladh <neia ikeran.org> writes:
On Wed, 09 Jan 2019 21:52:00 +0000, JN wrote:
 Perhaps, but sometimes it feels like the language features are set in
 stone at this point. As long as you can implement a language feature X
 with template magic, even if it's partially implementing X, it will be
 used as an excuse why X shouldn't be a language feature.
Things that give a little benefit over a template solution are guaranteed to be rejected. The solutions for named constructors are pretty clunky, on the other hand, either requiring use of reserved symbols or not supporting const fields, so a named constructors DIP is only likely to be rejected.
Jan 09 2019
prev sibling next sibling parent reply evilrat <evilrat666 gmail.com> writes:
On Wednesday, 9 January 2019 at 20:54:48 UTC, JN wrote:
 On Wednesday, 9 January 2019 at 10:02:12 UTC, bauss wrote:
 I like this approach to it and you're entirely correct.

 It would be nice with support for this in the language without 
 having to create factory methods.

 Basically something like the following could be lowered to 
 your code.
I was going to say that this post will be mostly ignored and people will present some template magic to partially implement such feature (just add few imports and some boilerplate code), but I guess I got beaten to it :)
Surprisingly no one was thinking about tags... Here is the one without templates struct Stuff_Normal {} struct Stuff_Special {} struct Stuff { this(Stuff_Normal _, float x, float y) { ... } this(Stuff_Special _, float x, float y) { ... } } usage auto val = Stuff(Stuff_Normal(), 1, 2); But honestly Atila version with distinct types was the best IMHO, because you know, one doesn't want to add temperature to angle or length to money
Jan 09 2019
parent reply Dru <dru notreal.com> writes:
 Here is the one without templates


 struct Stuff_Normal {}
 struct Stuff_Special {}

 struct Stuff
 {

   this(Stuff_Normal _, float x, float y) { ... }
   this(Stuff_Special _, float x, float y) { ... }
 }

 usage

   auto val = Stuff(Stuff_Normal(), 1, 2);
Not pretty syntax but I think this is the most practical solution :)
Jan 10 2019
parent evilrat <evilrat666 gmail.com> writes:
On Thursday, 10 January 2019 at 13:44:52 UTC, Dru wrote:
 Here is the one without templates


 struct Stuff_Normal {}
 struct Stuff_Special {}

 struct Stuff
 {

   this(Stuff_Normal _, float x, float y) { ... }
   this(Stuff_Special _, float x, float y) { ... }
 }

 usage

   auto val = Stuff(Stuff_Normal(), 1, 2);
Not pretty syntax but I think this is the most practical solution :)
well, you can also make factory function or static factory method that makes tags or just alias it to enum/constant to get rid of ctor call, also since tag parameter is unused name can be omitted (though this will make it look magical for someone unfamiliar with this use case)
Jan 10 2019
prev sibling parent Simen =?UTF-8?B?S2rDpnLDpXM=?= <simen.kjaras gmail.com> writes:
On Wednesday, 9 January 2019 at 20:54:48 UTC, JN wrote:
 On Wednesday, 9 January 2019 at 10:02:12 UTC, bauss wrote:
 I like this approach to it and you're entirely correct.

 It would be nice with support for this in the language without 
 having to create factory methods.

 Basically something like the following could be lowered to 
 your code.
I was going to say that this post will be mostly ignored and people will present some template magic to partially implement such feature (just add few imports and some boilerplate code), but I guess I got beaten to it :)
Since those templates are my specialty, here's my go: struct Point { this(string s : "rectangular")(real x, real y) { /* ... */ } this(string s : "polar") (real r, real theta) { /* ... */ } } struct make(T) { disable this(); static T opDispatch(string name, Args...)(Args args) { T result; result.__ctor!name(args); return result; } } unittest { auto a = make!Point.rectangular(1,2); auto b = make!Point.polar(1,2); } -- Simen
Jan 09 2019
prev sibling parent reply Neia Neutuladh <neia ikeran.org> writes:
On Wed, 09 Jan 2019 08:53:40 +0000, Alex wrote:
 On Wednesday, 9 January 2019 at 07:47:02 UTC, Dru wrote:
 Another way to distinguish between constructors is needed. Because it
 is possible to have two different constructors that take the same
 arguments.
 Adding dummy arguments that are unused hurts code clarity.
Couldn't this problem be solved by a factory method? I mean, either this, or something like this: http://www.cs.technion.ac.il/users/yechiel/c++-faq/named-ctor-idiom.html
A constructor can alter const fields, but a factory method can't, so they're not exactly equivalent. I was hoping you could add a template parameter to a constructor to give a "name" to it, like: class Foo { const int i; this(string name: "shift")(int i) { this.i = 1 << i; } this(string name: "direct")(int i) { this.i = i; } } new Foo!"shift"(3); But that doesn't work; there is no way to explicitly provide template parameters to a constructor.
Jan 09 2019
parent reply Daniel Kozak <kozzi11 gmail.com> writes:
On Wed, Jan 9, 2019 at 5:50 PM Neia Neutuladh via Digitalmars-d <
digitalmars-d puremagic.com> wrote:

 On Wed, 09 Jan 2019 08:53:40 +0000, Alex wrote:
 On Wednesday, 9 January 2019 at 07:47:02 UTC, Dru wrote:
 Another way to distinguish between constructors is needed. Because it
 is possible to have two different constructors that take the same
 arguments.
 Adding dummy arguments that are unused hurts code clarity.
Couldn't this problem be solved by a factory method? I mean, either this, or something like this: http://www.cs.technion.ac.il/users/yechiel/c++-faq/named-ctor-idiom.html
A constructor can alter const fields, but a factory method can't, so they're not exactly equivalent. I was hoping you could add a template parameter to a constructor to give a "name" to it, like: class Foo { const int i; this(string name: "shift")(int i) { this.i = 1 << i; } this(string name: "direct")(int i) { this.i = i; } } new Foo!"shift"(3); But that doesn't work; there is no way to explicitly provide template parameters to a constructor.
import std.stdio; import std.math; void main() { auto sa = S.constructorA(3,4); auto sb = S.constructorB(3,4); writeln(sa); writeln(sb); } struct S { public: this(string name: "constructorA")(real x, real y) { this.x = x; this.y = y; } this(string name: "constructorB")(real x, real z) { this.x = x; this.z = z; } static constructorA(Args...)(Args args) { S s; s.__ctor!"constructorA"(args); return s; } static constructorB(Args...)(Args args) { S s; s.__ctor!"constructorB"(args); return s; } const real x, y, z; }
Jan 09 2019
parent reply 12345swordy <alexanderheistermann gmail.com> writes:
On Wednesday, 9 January 2019 at 18:19:35 UTC, Daniel Kozak wrote:
 On Wed, Jan 9, 2019 at 5:50 PM Neia Neutuladh via Digitalmars-d 
 < digitalmars-d puremagic.com> wrote:

[...]
import std.stdio; import std.math; void main() { auto sa = S.constructorA(3,4); auto sb = S.constructorB(3,4); writeln(sa); writeln(sb); } struct S { public: this(string name: "constructorA")(real x, real y) { this.x = x; this.y = y; } this(string name: "constructorB")(real x, real z) { this.x = x; this.z = z; } static constructorA(Args...)(Args args) { S s; s.__ctor!"constructorA"(args); return s; } static constructorB(Args...)(Args args) { S s; s.__ctor!"constructorB"(args); return s; } const real x, y, z; }
That solution has a compile time cost.
Jan 09 2019
parent reply Daniel =?UTF-8?B?S296w6Fr?= <kozzi11 gmail.com> writes:
On Wednesday, 9 January 2019 at 18:24:28 UTC, 12345swordy wrote:
 That solution has a compile time cost.
I do not see any compile time cost, what do you mean?
Jan 09 2019
parent 12345swordy <alexanderheistermann gmail.com> writes:
On Wednesday, 9 January 2019 at 19:04:33 UTC, Daniel Kozák wrote:
 On Wednesday, 9 January 2019 at 18:24:28 UTC, 12345swordy wrote:
 That solution has a compile time cost.
I do not see any compile time cost, what do you mean?
You are using templates in that code, that going to have compile time associated with it(especially recursive templates). Regardless, your code uses the hidden symbol ._ctor! Which if you find yourself in a such situation then something is severely wrong. Alex
Jan 09 2019
prev sibling parent reply Atila Neves <atila.neves gmail.com> writes:
On Wednesday, 9 January 2019 at 07:47:02 UTC, Dru wrote:
 Another way to distinguish between constructors is needed.
 Because it is possible to have two different constructors that 
 take the same arguments.
 Adding dummy arguments that are unused hurts code clarity.
Adding dummy arguments is unecessary. Just use the type system: struct Person { this(FirstName firstName, LastName lastName) { /* ... */ } } struct FirstName { string value; } struct LastName { string value; } C++ even has two libraries to do this, but D doesn't need that given that structs in D are much more useful (no need to write a constructor).
Jan 09 2019
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, Jan 09, 2019 at 05:30:32PM +0000, Atila Neves via Digitalmars-d wrote:
 On Wednesday, 9 January 2019 at 07:47:02 UTC, Dru wrote:
 Another way to distinguish between constructors is needed.  Because
 it is possible to have two different constructors that take the same
 arguments.  Adding dummy arguments that are unused hurts code
 clarity.
Adding dummy arguments is unecessary. Just use the type system: struct Person { this(FirstName firstName, LastName lastName) { /* ... */ } } struct FirstName { string value; } struct LastName { string value; }
+1. That's what the type system is for. Dummy arguments are NEVER a good idea unless there's a need for the API to be uniform. It also makes the calling code self-documenting without needing language support for named arguments: auto myPerson = Person(FirstName("John"), LastName("Doe")); rather than the opaque (and thus error-prone): // Bug 1234: hmm, is it first name first, or last name first? //auto myPerson = Person("Doe", "John"); auto myPerson = Person("John", "Doe"); T -- Bare foot: (n.) A device for locating thumb tacks on the floor.
Jan 09 2019
next sibling parent reply Atila Neves <atila.neves gmail.com> writes:
On Wednesday, 9 January 2019 at 17:46:52 UTC, H. S. Teoh wrote:
 On Wed, Jan 09, 2019 at 05:30:32PM +0000, Atila Neves via 
 Digitalmars-d wrote:
 [...]
+1. That's what the type system is for. Dummy arguments are NEVER a good idea unless there's a need for the API to be uniform. It also makes the calling code self-documenting without needing language support for named arguments: auto myPerson = Person(FirstName("John"), LastName("Doe")); rather than the opaque (and thus error-prone): // Bug 1234: hmm, is it first name first, or last name first? //auto myPerson = Person("Doe", "John"); auto myPerson = Person("John", "Doe"); T
Fully in agreement. Now if only I write that library I've been meaning to that lets you pass parameters in any order if the types are right...
Jan 10 2019
next sibling parent reply evilrat <evilrat666 gmail.com> writes:
On Thursday, 10 January 2019 at 10:50:24 UTC, Atila Neves wrote:
 On Wednesday, 9 January 2019 at 17:46:52 UTC, H. S. Teoh wrote:
 On Wed, Jan 09, 2019 at 05:30:32PM +0000, Atila Neves via 
 Digitalmars-d wrote:
 [...]
+1. That's what the type system is for. Dummy arguments are NEVER a good idea unless there's a need for the API to be uniform. It also makes the calling code self-documenting without needing language support for named arguments: auto myPerson = Person(FirstName("John"), LastName("Doe")); rather than the opaque (and thus error-prone): // Bug 1234: hmm, is it first name first, or last name first? //auto myPerson = Person("Doe", "John"); auto myPerson = Person("John", "Doe"); T
Fully in agreement. Now if only I write that library I've been meaning to that lets you pass parameters in any order if the types are right...
For that I'd actually would prefer having a language support - void someFunc(string firstName, string lastName, int age); // use it someFunc(age=30, firstName="John", lastName="Doe"); That also solves the problem when we have some default parameters and only want to pass one of them from middle/end of the list.
Jan 10 2019
parent reply Atila Neves <atila.neves gmail.com> writes:
On Thursday, 10 January 2019 at 11:40:38 UTC, evilrat wrote:
 On Thursday, 10 January 2019 at 10:50:24 UTC, Atila Neves wrote:
 On Wednesday, 9 January 2019 at 17:46:52 UTC, H. S. Teoh wrote:
 On Wed, Jan 09, 2019 at 05:30:32PM +0000, Atila Neves via 
 Digitalmars-d wrote:
 [...]
+1. That's what the type system is for. Dummy arguments are NEVER a good idea unless there's a need for the API to be uniform. It also makes the calling code self-documenting without needing language support for named arguments: auto myPerson = Person(FirstName("John"), LastName("Doe")); rather than the opaque (and thus error-prone): // Bug 1234: hmm, is it first name first, or last name first? //auto myPerson = Person("Doe", "John"); auto myPerson = Person("John", "Doe"); T
Fully in agreement. Now if only I write that library I've been meaning to that lets you pass parameters in any order if the types are right...
For that I'd actually would prefer having a language support -
Language support isn't needed, and since this kind of feature doesn't play well with overloading... What's the _real_ difference between `someFunc(firstName="Alice")` and `someFunc(FirstName("Alice"))`? The former saves one character? Is that really worth the cost of language support? I have a feeling a lot of feature requests neglect that each and every one of them has a cost, and there's only enough budget for so many.
    void someFunc(string firstName, string lastName, int age);

    // use it
    someFunc(age=30, firstName="John", lastName="Doe");

 That also solves the problem when we have some default 
 parameters and only want to pass one of them from middle/end of 
 the list.
This can be solved with a library, which I mentioned above I want to write. You can do this today awkwardly by using a variadic template and reflecting.
Jan 10 2019
next sibling parent reply JN <666total wp.pl> writes:
On Thursday, 10 January 2019 at 13:53:40 UTC, Atila Neves wrote:
 What's the _real_ difference between 
 `someFunc(firstName="Alice")` and 
 `someFunc(FirstName("Alice"))`? The former saves one character? 
 Is that really worth the cost of language support? I have a 
 feeling a lot of feature requests neglect that each and every 
 one of them has a cost, and there's only enough budget for so 
 many.
Isn't there a performance cost added when using structs like that? And the real cost is the fact that you have to create all those structs meticulously and you can only use that with functions that expect such usage. With language support, you could have named arguments for every code there exists. Anyway, the fact that arguments are named are only a bonus, the real strength of named arguments comes with optional arguments. Look at the Builder pattern, it's pretty much a workaround for lack of named/keyword arguments. Also, named/keyword arguments shouldn't be an issue in regards to overloading and argument order. Every language I know that has named/kw arguments allows either only keyword arguments or keywords arguments at the end of the argument list. You can do: foo(x=10) foo(10, 20, z=30) but you can't do: foo(x=10, 20) because it'd be ambiguous.
Jan 10 2019
next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Thu, Jan 10, 2019 at 02:35:33PM +0000, JN via Digitalmars-d wrote:
 On Thursday, 10 January 2019 at 13:53:40 UTC, Atila Neves wrote:
 
 What's the _real_ difference between `someFunc(firstName="Alice")`
 and `someFunc(FirstName("Alice"))`? The former saves one character?
 Is that really worth the cost of language support? I have a feeling
 a lot of feature requests neglect that each and every one of them
 has a cost, and there's only enough budget for so many.
 
Isn't there a performance cost added when using structs like that?
I highly doubt it. Esp. with D's structs, which are designed to be "glorified ints", i.e., they are designed precisely to be passed as though they were their constituent fields. And anyway, it's nothing an optimizer can't eliminate the cost for (if there's any to begin with).
 And the real cost is the fact that you have to create all those
 structs meticulously and you can only use that with functions that
 expect such usage.
I started some time ago to sketch a generator template that uses introspection to auto-declare such structs for a function and generate overloads that accept named parameters in any order. Didn't have the time to finish it, unfortunately. But the idea was to make it as painless as possible to use this idiom.
 With language support, you could have named arguments for every code
 there exists.
Good point.
 Anyway, the fact that arguments are named are only a bonus, the real
 strength of named arguments comes with optional arguments. Look at the
 Builder pattern, it's pretty much a workaround for lack of
 named/keyword arguments.
[...] These days, whenever my functions start needing so many optional arguments that this becomes a problem, I just start using a struct to encapsulate them instead. I wouldn't claim it's the perfect solution, but it does pretty much have equivalent functionality: you can pass values in order (MyArgs(...)), or pass them by name (MyArgs args; args.field1 = 123;), and there can be default arguments (default field values). It does need a bit more typing, but other than that it works pretty well. And it even supports things a language-backed named argument feature can't handle: inheriting from another Args struct ("inheriting" another function's arguments) and easy passing between a set of functions that share the same set of parameters (just pass the struct instead of 10 arguments each time). And just to be clear, I'm not *against* adding named arguments to D. Just saying that you *can* get pretty far with what's currently there. T -- Change is inevitable, except from a vending machine.
Jan 10 2019
parent JN <666total wp.pl> writes:
On Thursday, 10 January 2019 at 16:22:27 UTC, H. S. Teoh wrote:
 These days, whenever my functions start needing so many 
 optional arguments that this becomes a problem, I just start 
 using a struct to encapsulate them instead. I wouldn't claim 
 it's the perfect solution, but it does pretty much have 
 equivalent functionality: you can pass values in order 
 (MyArgs(...)), or pass them by name (MyArgs args; args.field1 = 
 123;), and there can be default arguments (default field 
 values). It does need a bit more typing, but other than that it 
 works pretty well.
This is a viable solution. It works for many usecases. APIs like Vulkan and Direct3D like to use the struct solution to pass arguments to their methods. https://github.com/wilzbach/DIPs/blob/b1283b455b635d7dcbc2c871d2aa47cc6719005 /DIPs/DIP1xxx-sw.md would make it much sweeter to work with structs though.
Jan 10 2019
prev sibling next sibling parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 01/10/2019 06:35 AM, JN wrote:

 the real cost is the fact that you have to create all those structs
 meticulously and you can only use that with functions that expect such
 usage.
Weka uses the generic TypedIdentifier for integers: https://github.com/weka-io/mecca/blob/master/src/mecca/lib/typedid.d#L532 Ali
Jan 10 2019
parent Neia Neutuladh <neia ikeran.org> writes:
On Thu, 10 Jan 2019 10:12:24 -0800, Ali Çehreli wrote:

 On 01/10/2019 06:35 AM, JN wrote:
 
  > the real cost is the fact that you have to create all those structs
  > meticulously and you can only use that with functions that expect
  > such usage.
 
 Weka uses the generic TypedIdentifier for integers:
 
    https://github.com/weka-io/mecca/blob/master/src/mecca/lib/
typedid.d#L532
 
 Ali
Which can be done equally well with std.typecons.Typedef. It still has a lot of the same issues as custom structs, but at least you can use a different module's named arg structs as long as they're not doing anything funky. Here's a slightly better version: --- struct Named(T) { template opDispatch(string name) { alias opDispatch = Typedef!(T, T.init, name); } } struct Arg { template opDispatch(string name) { alias opDispatch = makeTypedef!name; } } template makeTypedef(string name) { Typedef!(T, T.init, name) makeTypedef(T)(T value) { return Typedef!(T, T.init, name)(value); } } --- Usage: --- void foo(Named!int.age a, Named!string.name b) { writefln("name: %s age: %s", b, a); } void main() { foo(Arg.age(12), Arg.name("Rydia")); } --- Something like that is the best you're likely to get with the struct-per- argument model.
Jan 10 2019
prev sibling next sibling parent reply Atila Neves <atila.neves gmail.com> writes:
On Thursday, 10 January 2019 at 14:35:33 UTC, JN wrote:
 On Thursday, 10 January 2019 at 13:53:40 UTC, Atila Neves wrote:
 What's the _real_ difference between 
 `someFunc(firstName="Alice")` and 
 `someFunc(FirstName("Alice"))`? The former saves one 
 character? Is that really worth the cost of language support? 
 I have a feeling a lot of feature requests neglect that each 
 and every one of them has a cost, and there's only enough 
 budget for so many.
Isn't there a performance cost added when using structs like that?
No.
 And the real cost is the fact that you have to create all those 
 structs meticulously
Meticulously? It's a name and the type you were going to write anyway. You can always call the field `_` to avoid even more typing.
 and you can only use that with functions that expect such usage.
Yes. But I'm writing the API.
 With language support, you could have named arguments for every 
 code there exists.
I'd love to see a DIP that did that and dealt with all the corner cases. I suspect that it's not as easy as implied here.
 Anyway, the fact that arguments are named are only a bonus, the 
 real strength of named arguments comes with optional arguments.
Again, I want a library for this. I know how to write it, I just haven't yet.
 Look at the Builder pattern, it's pretty much a workaround for 
 lack of named/keyword arguments.
Java doesn't have variadic templates, D does. No builder pattern needed.
 Also, named/keyword arguments shouldn't be an issue in regards 
 to overloading and argument order. Every language I know that 
 has named/kw arguments allows either only keyword arguments or 
 keywords arguments at the end of the argument list. You can do:

 foo(x=10)
 foo(10, 20, z=30)

 but you can't do:

 foo(x=10, 20)

 because it'd be ambiguous.
I don't know of any language that does that and has overloading. Walter knows way more about this than I do.
Jan 10 2019
parent reply aliak <something something.com> writes:
On Thursday, 10 January 2019 at 18:27:59 UTC, Atila Neves wrote:
 Look at the Builder pattern, it's pretty much a workaround for 
 lack of named/keyword arguments.
Java doesn't have variadic templates, D does. No builder pattern needed.
Not sure how variadic templates solves the builder pattern? How do you deal with a mixture of arguments of similar and different types with some being optional and not? auto a = Builder() .accountNumber(202) .firstName("Bob") .lastName("Who") .branch("N-2") .interest(20.0) .balance(10.0) .build() ??
 Also, named/keyword arguments shouldn't be an issue in regards 
 to overloading and argument order. Every language I know that 
 has named/kw arguments allows either only keyword arguments or 
 keywords arguments at the end of the argument list. You can do:

 foo(x=10)
 foo(10, 20, z=30)

 but you can't do:

 foo(x=10, 20)

 because it'd be ambiguous.
I don't know of any language that does that and has overloading. Walter knows way more about this than I do.
Swift. Named arguments. Overloading. Executed perfectly IMO: For the case mentioned above: func f(x: Int, _ y: Int) {} func main() { f(x: 10, 20) } Unless he means specifically with using named arguments with any order. Then yeah nah. Swift no can do. But named arguments followed by unnamed and vice versa plus overloading all good.
Jan 14 2019
next sibling parent reply Atila Neves <atila.neves gmail.com> writes:
On Monday, 14 January 2019 at 14:31:09 UTC, aliak wrote:
 On Thursday, 10 January 2019 at 18:27:59 UTC, Atila Neves wrote:
 Look at the Builder pattern, it's pretty much a workaround 
 for lack of named/keyword arguments.
Java doesn't have variadic templates, D does. No builder pattern needed.
Not sure how variadic templates solves the builder pattern? How do you deal with a mixture of arguments of similar and different types with some being optional and not? auto a = Builder() .accountNumber(202) .firstName("Bob") .lastName("Who") .branch("N-2") .interest(20.0) .balance(10.0) .build() ??
auto a = Customer(AccountNumber(202), FirstName("Bob"), LastName("Who"), Branch("N-2"), Interest(20.0), Balance(10.0)); struct Customer { this(A...)(auto ref A args) if(constraintVerifyingNumberAndTypesOfArgs!A) { this.accountNumber = args[staticIndexOf!(AccountNumber, A)]; // etc. } } If staticIndexOf is negative, assign a default value. Again, I want to write a library that does this for me based on a declaration that looks like a regular function / template.
 I don't know of any language that does that and has 
 overloading. Walter knows way more about this than I do.
Swift. Named arguments. Overloading. Executed perfectly IMO:
I can't comment. I don't know anything about Swift.
 Unless he means specifically with using named arguments with 
 any order. Then yeah nah. Swift no can do. But named arguments 
 followed by unnamed and vice versa plus overloading all good.
Probably? I don't really know.
Jan 14 2019
next sibling parent 12345swordy <alexanderheistermann gmail.com> writes:
On Monday, 14 January 2019 at 16:04:26 UTC, Atila Neves wrote:
 Swift. Named arguments. Overloading. Executed perfectly IMO:
I can't comment. I don't know anything about Swift.
https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/named-and-optional-arguments
Jan 14 2019
prev sibling parent reply Olivier FAURE <couteaubleu gmail.com> writes:
On Monday, 14 January 2019 at 16:04:26 UTC, Atila Neves wrote:
 On Monday, 14 January 2019 at 14:31:09 UTC, aliak wrote:
 auto a = Builder()
   .accountNumber(202)
   .firstName("Bob")
   .lastName("Who")
   .branch("N-2")
   .interest(20.0)
   .balance(10.0)
   .build()

 ??
auto a = Customer(AccountNumber(202), FirstName("Bob"), LastName("Who"), Branch("N-2"), Interest(20.0), Balance(10.0)); struct Customer { this(A...)(auto ref A args) if(constraintVerifyingNumberAndTypesOfArgs!A) { this.accountNumber = args[staticIndexOf!(AccountNumber, A)]; // etc. } }
I think these design patterns are useful if you really want named arguments in your code right now, but they really shouldn't replace the structure initialization DIP. Personally speaking, I hate having hacks like these in my code and would immensely prefer an idiomatic syntax eg: auto a = Customer{ accountNumber: 202, fistName: "Bob" }; which would produce easy-to-read error messages like: Error: `fistName` is not a member of `Customer`. Did you mean `firstName`? Error: Initialization list for `Customer` does not provide value for member variable `lastName`, which has no default value defined. I'm having trouble guessing what error messages your Customer structure would provide, but I imagine they wouldn't be nearly as informative. Same thing for generated documentation.
Jan 15 2019
parent Atila Neves <atila.neves gmail.com> writes:
On Tuesday, 15 January 2019 at 12:42:32 UTC, Olivier FAURE wrote:
 On Monday, 14 January 2019 at 16:04:26 UTC, Atila Neves wrote:
 [...]
I think these design patterns are useful if you really want named arguments in your code right now, but they really shouldn't replace the structure initialization DIP. [...]
Good error messages are perfectly doable with a library solution. Sure, the "Did you mean" one is harder to implement, but still doable.
Jan 15 2019
prev sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2019-01-14 15:31, aliak wrote:

 Swift. Named arguments. Overloading. Executed perfectly IMO:
Technically it's not function overloading. The functions have separate names, the parameter keys are part of the name. That's at least how it works in Objective-C. -- /Jacob Carlborg
Jan 14 2019
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 1/14/19 3:05 PM, Jacob Carlborg wrote:
 On 2019-01-14 15:31, aliak wrote:
 
 Swift. Named arguments. Overloading. Executed perfectly IMO:
Technically it's not function overloading. The functions have separate names, the parameter keys are part of the name. That's at least how it works in Objective-C.
I was going to say that, but I feel like possibliy in my dealings with Swift (which isn't huge), it may have had more abilities for overloading than objective-C. -Steve
Jan 14 2019
parent aliak <something something.com> writes:
On Monday, 14 January 2019 at 20:59:11 UTC, Steven Schveighoffer 
wrote:
 On 1/14/19 3:05 PM, Jacob Carlborg wrote:
 On 2019-01-14 15:31, aliak wrote:
 
 Swift. Named arguments. Overloading. Executed perfectly IMO:
Technically it's not function overloading. The functions have separate names, the parameter keys are part of the name. That's at least how it works in Objective-C.
I was going to say that, but I feel like possibliy in my dealings with Swift (which isn't huge), it may have had more abilities for overloading than objective-C. -Steve
You can use the same keys with different types and call the correct function based on argument type. You can even overload based on return type alone! I don't remember if that was possible in objective-c or not - flushed from me system :p
Jan 14 2019
prev sibling parent reply rikki cattermole <rikki cattermole.co.nz> writes:
On 11/01/2019 3:35 AM, JN wrote:
 Also, named/keyword arguments shouldn't be an issue in regards to 
 overloading and argument order. Every language I know that has named/kw 
 arguments allows either only keyword arguments or keywords arguments at 
 the end of the argument list. You can do:
 
 foo(x=10)
 foo(10, 20, z=30)
 
 but you can't do:
 
 foo(x=10, 20)
 
 because it'd be ambiguous.
Actually every example there is ambiguous and problematic in D. It conflicts with AssignExpression which is identical in syntax and usage!
Jan 10 2019
parent reply JN <666total wp.pl> writes:
On Friday, 11 January 2019 at 07:53:56 UTC, rikki cattermole 
wrote:
 On 11/01/2019 3:35 AM, JN wrote:
 Also, named/keyword arguments shouldn't be an issue in regards 
 to overloading and argument order. Every language I know that 
 has named/kw arguments allows either only keyword arguments or 
 keywords arguments at the end of the argument list. You can do:
 
 foo(x=10)
 foo(10, 20, z=30)
 
 but you can't do:
 
 foo(x=10, 20)
 
 because it'd be ambiguous.
Actually every example there is ambiguous and problematic in D. It conflicts with AssignExpression which is identical in syntax and usage!
use :, I guess: foo(x:10, 20) would be better?
Jan 11 2019
parent reply rikki cattermole <rikki cattermole.co.nz> writes:
On 12/01/2019 12:24 AM, JN wrote:
 On Friday, 11 January 2019 at 07:53:56 UTC, rikki cattermole wrote:
 On 11/01/2019 3:35 AM, JN wrote:
 Also, named/keyword arguments shouldn't be an issue in regards to 
 overloading and argument order. Every language I know that has 
 named/kw arguments allows either only keyword arguments or keywords 
 arguments at the end of the argument list. You can do:

 foo(x=10)
 foo(10, 20, z=30)

 but you can't do:

 foo(x=10, 20)

 because it'd be ambiguous.
Actually every example there is ambiguous and problematic in D. It conflicts with AssignExpression which is identical in syntax and usage!
I guess: foo(x:10, 20) would be better?
Yup no ambiguity, that is why my named parameter DIP uses it :)
Jan 11 2019
parent reply JN <666total wp.pl> writes:
On Friday, 11 January 2019 at 11:25:39 UTC, rikki cattermole 
wrote:
 On 12/01/2019 12:24 AM, JN wrote:
 On Friday, 11 January 2019 at 07:53:56 UTC, rikki cattermole 
 wrote:
 On 11/01/2019 3:35 AM, JN wrote:
 Also, named/keyword arguments shouldn't be an issue in 
 regards to overloading and argument order. Every language I 
 know that has named/kw arguments allows either only keyword 
 arguments or keywords arguments at the end of the argument 
 list. You can do:

 foo(x=10)
 foo(10, 20, z=30)

 but you can't do:

 foo(x=10, 20)

 because it'd be ambiguous.
Actually every example there is ambiguous and problematic in D. It conflicts with AssignExpression which is identical in syntax and usage!
use :, I guess: foo(x:10, 20) would be better?
Yup no ambiguity, that is why my named parameter DIP uses it :)
Is there even any usecase for AssignExpression within function parameters list? I imagine if (ptr = getPtr()) stuff can be useful (but still kind of risky if you actually intended ==), but putting assignments in function calls seems like could be disallowed without any negative effects.
Jan 11 2019
parent rikki cattermole <rikki cattermole.co.nz> writes:
On 12/01/2019 1:57 AM, JN wrote:
 On Friday, 11 January 2019 at 11:25:39 UTC, rikki cattermole wrote:
 On 12/01/2019 12:24 AM, JN wrote:
 On Friday, 11 January 2019 at 07:53:56 UTC, rikki cattermole wrote:
 On 11/01/2019 3:35 AM, JN wrote:
 Also, named/keyword arguments shouldn't be an issue in regards to 
 overloading and argument order. Every language I know that has 
 named/kw arguments allows either only keyword arguments or keywords 
 arguments at the end of the argument list. You can do:

 foo(x=10)
 foo(10, 20, z=30)

 but you can't do:

 foo(x=10, 20)

 because it'd be ambiguous.
Actually every example there is ambiguous and problematic in D. It conflicts with AssignExpression which is identical in syntax and usage!
I guess: foo(x:10, 20) would be better?
Yup no ambiguity, that is why my named parameter DIP uses it :)
Is there even any usecase for AssignExpression within function parameters list? I imagine if (ptr = getPtr()) stuff can be useful (but still kind of risky if you actually intended ==), but putting assignments in function calls seems like could be disallowed without any negative effects.
Our grammar looks very similar to C's in this area. In both languages there are no assignment statements, only assignment expressions with expressions promoted to statements directly. While we can make using AssignExpression like this illegal, it does make transition and porting to D harder from C. Perhaps Walter might be open to this? But personally while I might hate it, I don't think that it would be a good idea to remove it then use the same syntax for something else. So use case? C does it like this. That should be enough to keep it as is sadly.
Jan 11 2019
prev sibling parent Neia Neutuladh <neia ikeran.org> writes:
On Thu, 10 Jan 2019 13:53:40 +0000, Atila Neves wrote:
 What's the _real_ difference between `someFunc(firstName="Alice")` and
 `someFunc(FirstName("Alice"))`?
 The former saves one character?
It means you don't need to declare a new struct for every function parameter. Even if you are willing to declare a new struct for every function parameter, it means you don't have to do the work of either creating a unique name for each one (which would be annoying to users) or manually garbage collecting the structs as your code evolves (in case a parameter struct is used by multiple functions). Even if you're willing to do the work of declaring a new struct for each paramater, using short names, and manually garbage collecting them, language-provided named parameters mean that your end users don't need to worry about name collisions between two modules using struct wrappers as named parameters. Even if you're willing to do the work of declaring a new struct for each parameter, with short names and manually maintaining a minimal set of parameter structs, and you're willing to make your code's consumers deal with naming conflicts, you still don't get reorderable parameters. For that, you need to turn a function into a wrapper template that accepts parameters in any order and forwards them to an implementation function in a fixed order. This makes it difficult to take the address of the function. So, no, just using wrapper structs is not a serious alternative.
Jan 10 2019
prev sibling next sibling parent reply John Colvin <john.loughran.colvin gmail.com> writes:
On Thursday, 10 January 2019 at 10:50:24 UTC, Atila Neves wrote:
 On Wednesday, 9 January 2019 at 17:46:52 UTC, H. S. Teoh wrote:
 [...]
Fully in agreement. Now if only I write that library I've been meaning to that lets you pass parameters in any order if the types are right...
And doesn't cause compile times to explode? :)
Jan 10 2019
next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Thu, Jan 10, 2019 at 12:04:40PM +0000, John Colvin via Digitalmars-d wrote:
 On Thursday, 10 January 2019 at 10:50:24 UTC, Atila Neves wrote:
 On Wednesday, 9 January 2019 at 17:46:52 UTC, H. S. Teoh wrote:
 [...]
Fully in agreement. Now if only I write that library I've been meaning to that lets you pass parameters in any order if the types are right...
And doesn't cause compile times to explode? :)
Don't worry, this is Atila we're talking about. If he comes up with any solution, you can be sure it will not hurt his precious compile times! ;-) T -- IBM = I Blame Microsoft
Jan 10 2019
parent reply Atila Neves <atila.neves gmail.com> writes:
On Thursday, 10 January 2019 at 12:37:38 UTC, H. S. Teoh wrote:
 On Thu, Jan 10, 2019 at 12:04:40PM +0000, John Colvin via 
 Digitalmars-d wrote:
 On Thursday, 10 January 2019 at 10:50:24 UTC, Atila Neves 
 wrote:
 On Wednesday, 9 January 2019 at 17:46:52 UTC, H. S. Teoh 
 wrote:
 [...]
Fully in agreement. Now if only I write that library I've been meaning to that lets you pass parameters in any order if the types are right...
And doesn't cause compile times to explode? :)
Don't worry, this is Atila we're talking about. If he comes up with any solution, you can be sure it will not hurt his precious compile times! ;-)
Actually, they're so bad right now anyway from a baseline of acceptability to me that nothing I add on top makes it that much worse. Currently I'm trying to work out an efficient way to interpret D to run unit tests so I don't have to pay the linker tax and just avoid compiling code whilst developing. It'll be a lot of work, though... :(
Jan 10 2019
next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Thu, Jan 10, 2019 at 02:00:25PM +0000, Atila Neves via Digitalmars-d wrote:
 On Thursday, 10 January 2019 at 12:37:38 UTC, H. S. Teoh wrote:
[...]
 Don't worry, this is Atila we're talking about. If he comes up with
 any solution, you can be sure it will not hurt his precious compile
 times!  ;-)
Actually, they're so bad right now anyway from a baseline of acceptability to me that nothing I add on top makes it that much worse. Currently I'm trying to work out an efficient way to interpret D to run unit tests so I don't have to pay the linker tax and just avoid compiling code whilst developing. It'll be a lot of work, though... :(
Recently I've started to compile unittests separately from the final executable. The default behaviour of running unittests before main() is nice for one-off scripts and short-term projects, but for serious code I'm finding more and more that it makes more sense to compile and run unittests separately. I haven't gotten to the point of evading linker tax, though. But I surmise it should be relatively easy to just compile modules separately with -unittest -run, using -i to automatically pick up imports. Put this in a test target in your build script and have the tests built and run incrementally. Not sure how much time will be saved, due to -i potentially causing the same tests to run multiple times. I should try this out on one of my larger projects to see how it does. T -- They pretend to pay us, and we pretend to work. -- Russian saying
Jan 10 2019
parent reply Atila Neves <atila.neves gmail.com> writes:
On Thursday, 10 January 2019 at 16:33:51 UTC, H. S. Teoh wrote:
 On Thu, Jan 10, 2019 at 02:00:25PM +0000, Atila Neves via 
 Digitalmars-d wrote:
 [...]
[...]
 [...]
Recently I've started to compile unittests separately from the final executable. The default behaviour of running unittests before main() is nice for one-off scripts and short-term projects, but for serious code I'm finding more and more that it makes more sense to compile and run unittests separately. I haven't gotten to the point of evading linker tax, though. But I surmise it should be relatively easy to just compile modules separately with -unittest -run, using -i to automatically pick up imports. Put this in a test target in your build script and have the tests built and run incrementally. Not sure how much time will be saved, due to -i potentially causing the same tests to run multiple times. I should try this out on one of my larger projects to see how it does. T
I _only_ build unit tests apart from the production code now. I'm toying with not using `unittest` at all since I can reflect and find tests on my own, and I don't have to pay the compile time tax of importing a phobos module with `-unittest`.
Jan 10 2019
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Thu, Jan 10, 2019 at 06:29:27PM +0000, Atila Neves via Digitalmars-d wrote:
 On Thursday, 10 January 2019 at 16:33:51 UTC, H. S. Teoh wrote:
[...]
 Recently I've started to compile unittests separately from the final
 executable. The default behaviour of running unittests before main()
 is nice for one-off scripts and short-term projects, but for serious
 code I'm finding more and more that it makes more sense to compile
 and run unittests separately.  I haven't gotten to the point of
 evading linker tax, though.
 
 But I surmise it should be relatively easy to just compile modules
 separately with -unittest -run, using -i to automatically pick up
 imports. Put this in a test target in your build script and have the
 tests built and run incrementally. Not sure how much time will be
 saved, due to -i potentially causing the same tests to run multiple
 times. I should try this out on one of my larger projects to see how
 it does.
[...]
 I _only_ build unit tests apart from the production code now. I'm
 toying with not using `unittest` at all since I can reflect and find
 tests on my own, and I don't have to pay the compile time tax of
 importing a phobos module with `-unittest`.
The last time somebody looked, the real problem wasn't that Phobos (or any 3rd party library) unittests were being compiled over and over; it was caused by a change in template instantiation behaviour when -unittest is specified, such that many more templates are instantiated that probably don't need to be, thus causing the observed compile-time spike. There's a reason it was done this way, but I'm here gnashing my teeth wishing that there was a way to turn off this annoying behaviour. T -- Why waste time learning, when ignorance is instantaneous? -- Hobbes, from Calvin & Hobbes
Jan 10 2019
parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 1/10/19 1:49 PM, H. S. Teoh wrote:

 The last time somebody looked, the real problem wasn't that Phobos (or
 any 3rd party library) unittests were being compiled over and over; it
 was caused by a change in template instantiation behaviour when
 -unittest is specified, such that many more templates are instantiated
 that probably don't need to be, thus causing the observed compile-time
 spike.  There's a reason it was done this way, but I'm here gnashing my
 teeth wishing that there was a way to turn off this annoying behaviour.
Yes, it's that when unittest is enabled effectively -allinst is enabled as well. That wasn't the only problem, but it was the main reason why importing e.g. std.regex with -unittest was a huge penalty. -Steve
Jan 10 2019
prev sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2019-01-10 15:00, Atila Neves wrote:


 Currently I'm trying to work out an efficient way to interpret D to run 
 unit tests so I don't have to pay the linker tax and just avoid 
 compiling code whilst developing.
LDC supports JIT. -- /Jacob Carlborg
Jan 13 2019
parent reply Atila Neves <atila.neves gmail.com> writes:
On Sunday, 13 January 2019 at 20:59:42 UTC, Jacob Carlborg wrote:
 On 2019-01-10 15:00, Atila Neves wrote:


 Currently I'm trying to work out an efficient way to interpret 
 D to run unit tests so I don't have to pay the linker tax and 
 just avoid compiling code whilst developing.
LDC supports JIT.
I don't think that'd be useful for me. The only thing I care about is the latency between hitting a key and having feedback on my tests. It's rare that I have tests that would be significantly faster by optimising the machine code running on the CPU. The main problem is that I don't really know, and can't unless I measure. Which means implementing it somehow. Sigh.
Jan 14 2019
parent reply Jacob Carlborg <doob me.com> writes:
On 2019-01-14 14:54, Atila Neves wrote:

 I don't think that'd be useful for me. The only thing I care about is 
 the latency between hitting a key and having feedback on my tests. It's 
 rare that I have tests that would be significantly faster by optimising 
 the machine code running on the CPU.
I didn't mean to use a JIT to optimize the execution. I was more thinking of using LDC as a JIT to avoid having to implement your own interpreter. But the JIT might be too slow to compile the code. Hmm, can use use the CTFE interpreter in DMD? Hack that to use it for none CTFE stuff as well. -- /Jacob Carlborg
Jan 14 2019
parent Atila Neves <atila.neves gmail.com> writes:
On Monday, 14 January 2019 at 20:02:55 UTC, Jacob Carlborg wrote:
 On 2019-01-14 14:54, Atila Neves wrote:

 I don't think that'd be useful for me. The only thing I care 
 about is the latency between hitting a key and having feedback 
 on my tests. It's rare that I have tests that would be 
 significantly faster by optimising the machine code running on 
 the CPU.
I didn't mean to use a JIT to optimize the execution. I was more thinking of using LDC as a JIT to avoid having to implement your own interpreter. But the JIT might be too slow to compile the code. Hmm, can use use the CTFE interpreter in DMD? Hack that to use it for none CTFE stuff as well.
I tried that first - many of the declarations are private and it was a pain to reuse the code. Then I started writing an interpreter from scratch and realised very soon how much work that would be. I need to try and reuse the existing code again. I'm also not sure how to deal with imported code from dependencies yet and am a bit in analysis paralysis.
Jan 15 2019
prev sibling parent Atila Neves <atila.neves gmail.com> writes:
On Thursday, 10 January 2019 at 12:04:40 UTC, John Colvin wrote:
 On Thursday, 10 January 2019 at 10:50:24 UTC, Atila Neves wrote:
 On Wednesday, 9 January 2019 at 17:46:52 UTC, H. S. Teoh wrote:
 [...]
Fully in agreement. Now if only I write that library I've been meaning to that lets you pass parameters in any order if the types are right...
And doesn't cause compile times to explode? :)
I'm not a wizard! ;)
Jan 10 2019
prev sibling parent jmh530 <john.michael.hall gmail.com> writes:
On Thursday, 10 January 2019 at 10:50:24 UTC, Atila Neves wrote:
 [snip]

 Fully in agreement. Now if only I write that library I've been 
 meaning to that lets you pass parameters in any order if the 
 types are right...
That becomes a lot more interesting when you give specific types to each function parameter. You can also have one version of the function using basic types and then another version with the specific types and then just pass the specific stuff to the basic version. Of course with D you could generate all the stuff with specific types with a mixin...
Jan 10 2019
prev sibling parent Walter Bright <newshound2 digitalmars.com> writes:
On 1/9/2019 9:46 AM, H. S. Teoh wrote:
 It also makes the calling code self-documenting without needing language
 support for named arguments:
 
 	auto myPerson = Person(FirstName("John"), LastName("Doe"));
Nailed it :-)
Jan 10 2019