www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Named arguments via struct initialization in functions

reply Seb <seb wilzba.ch> writes:
Hey all,

I wanted to relive the discussion on named arguments and ping for 
its current status.

There is a bunch of examples to show how needed a unified 
solution for this problem is, let me give you one from phobos [2].

```
// I want to allow downsizing
iota(10).sliced!(Yes.replaceArrayWithPointer, 
Yes.allowDownsize)(4);
```

There is of course the alternative solution that an author 
overloads his function to the utmost, but this results in 
complexity and duplicated code (see e.g. redBlackTree in phobos 
[3]).

Currently the best solution AFAICT is to use a struct to pass 
such flags, like

```
struct Options{int x; int y=1; int z=2;}
auto fun(Options options)
{
     return options.x + options.y + options.z;
}

Options options = {x: 4, z: 3};
auto a=fun(options);
```

There are also other workarounds as discussed in [1] (e.g. with 
CTFE string analysis [4]).

I general there two solutions to this problem
1) get true named parameters support in D (probably complicated)
2) allow struct inits in functions - e.g. fun({x: 4})

For 2) Jacob Carlborg has proposed something similar three years 
ago. In his case he proposed anonymous structs which might be 
more generally applicable, however just created the struct seems 
easier and allows more
It doesn't seem that complicated to me as the compiler already 
knows the type of the argument.

Using structs is not ideal, because one can't require parameters, 
but this can be solved by having those parameters as normal ones 
like `sliced(4, {allowDownsize: true})` and it creates some maybe 
unnecessary overhead.
However it is probably the easiest solution right now.

What are your thoughts on this issue?

On a side note: many templated functions are also complicated and 
experiencing this issue, so it also might be worth to think about 
this issue too ;-)

Cheers,

Seb

[1] 
http://forum.dlang.org/post/pxndhoskpjxvnoacajaz forum.dlang.org
[2] https://github.com/DlangScience/mir/issues/18
[3] 
https://github.com/D-Programming-Language/phobos/pull/4041/files
[4] 
https://github.com/timotheecour/dtools/blob/master/dtools/util/functional.d
Mar 06 2016
next sibling parent reply Chris Wright <dhasenan gmail.com> writes:
On Sun, 06 Mar 2016 17:35:38 +0000, Seb wrote:

 Hey all,
 
 I wanted to relive the discussion on named arguments and ping for its
 current status.
We want something to let people provide arguments sparsely and unambiguously, and if possible in arbitrary order. The existing DIP just lets you provide compiler-checked annotations to verify the parameter names match your expectations, assuming the writer of the function chose to allow it. Nobody much liked it. I've got a draft DIP on my hard drive to provide actual named arguments but haven't gotten around to proposing it. There was considerable disagreement on whether the function parameters should be explicitly marked in order to use them as named or not. Scala, Python, C#, Ceylon, and I believe Ada don't require you to mark them. Ruby and Dart require you to mark them. There are a couple other interesting decisions regarding named parameters, but nobody much considered them during the discussion. Ceylon's the most permissive example.
 2) allow struct inits in functions - e.g. fun({x: 4})
I looked into this a bit. I've got another draft DIP for it on my hard drive. There are two obvious ambiguities: struct A { int x; long y; } struct B { int x; string y; } void foo(A a) {} void foo(B b) {} foo({x: 1}); There's no way to tell which overload to invoke. Then let's say I added: void foo(void delegate() dg) {} foo({}); That could be a default-initialized struct of type A or B, or it could be a no-op delegate. You can resolve that with a slightly different syntax: foo(A{x: 1}); I think I noticed an ambiguity with that, too, but I can't recall now. I failed to write it down in any case. Anyway, if it works, it's an improvement. And you only need explicit type marking at the top level; it's unambiguous after that.
Mar 06 2016
parent reply ZombineDev <petar.p.kirov gmail.com> writes:
On Sunday, 6 March 2016 at 19:10:50 UTC, Chris Wright wrote:
 On Sun, 06 Mar 2016 17:35:38 +0000, Seb wrote:

 [...]
We want something to let people provide arguments sparsely and unambiguously, and if possible in arbitrary order. The existing DIP just lets you provide compiler-checked annotations to verify the parameter names match your expectations, assuming the writer of the function chose to allow it. Nobody much liked it. I've got a draft DIP on my hard drive to provide actual named arguments but haven't gotten around to proposing it. There was considerable disagreement on whether the function parameters should be explicitly marked in order to use them as named or not. Scala, Python, C#, Ceylon, and I believe Ada don't require you to mark them. Ruby and Dart require you to mark them. There are a couple other interesting decisions regarding named parameters, but nobody much considered them during the discussion. Ceylon's the most permissive example.
 [...]
I looked into this a bit. I've got another draft DIP for it on my hard drive. There are two obvious ambiguities: struct A { int x; long y; } struct B { int x; string y; } void foo(A a) {} void foo(B b) {} foo({x: 1}); There's no way to tell which overload to invoke. Then let's say I added: void foo(void delegate() dg) {} foo({}); That could be a default-initialized struct of type A or B, or it could be a no-op delegate. You can resolve that with a slightly different syntax: foo(A{x: 1}); I think I noticed an ambiguity with that, too, but I can't recall now. I failed to write it down in any case.
The compiler should detect that this call is ambiguous and would not allow it. To resolve the ambiguity, the user can write: foo(A{ x: 1 }); foo(B{ x: 1 }); foo(() { }); foo(delegate void() { }); Which I think is a very reasonably requirement. Do you have any other examples of ambiguity that can't be easily resolved?
 Anyway, if it works, it's an improvement.
Yep.
 And you only need explicit type marking at the top level;
 it's unambiguous after that.
I'm not sure what you mean by "explicit type marking at the top level".
Mar 07 2016
parent reply Chris Wright <dhasenan gmail.com> writes:
On Mon, 07 Mar 2016 11:06:13 +0000, ZombineDev wrote:
 The compiler should detect that this call is ambiguous and would not
 allow it.
It's a potentially significant amount of work to determine that the expression is ambiguous, or to disambiguate.
 To resolve the ambiguity, the user can write:
 foo(A{ x: 1 });
That's why I suggested it.
 And you only need explicit type marking at the top level;
 it's unambiguous after that.
I'm not sure what you mean by "explicit type marking at the top level".
It always is unambiguous to write things like: auto a = A{x: {y: 1}};
Mar 07 2016
parent reply ZombineDev <petar.p.kirov gmail.com> writes:
On Monday, 7 March 2016 at 19:06:54 UTC, Chris Wright wrote:
 On Mon, 07 Mar 2016 11:06:13 +0000, ZombineDev wrote:
 The compiler should detect that this call is ambiguous and 
 would not allow it.
It's a potentially significant amount of work to determine that the expression is ambiguous, or to disambiguate.
I don't think so. At least not much more than what currently the compiler does for overload resolution (comparing the void(A) overloard to the void(B)). Tuples literals / struct initializer literals / delegates should be classified before the end of the parsing phase (which is actually at least an order of magnitude faster than the semantic phase).
 To resolve the ambiguity, the user can write:
 foo(A{ x: 1 });
That's why I suggested it.
 And you only need explicit type marking at the top level; 
 it's unambiguous after that.
I'm not sure what you mean by "explicit type marking at the top level".
It always is unambiguous to write things like: auto a = A{x: {y: 1}};
Ah ok, agreed.
Mar 10 2016
parent reply Chris Wright <dhasenan gmail.com> writes:
On Thu, 10 Mar 2016 19:01:53 +0000, ZombineDev wrote:

 On Monday, 7 March 2016 at 19:06:54 UTC, Chris Wright wrote:
 On Mon, 07 Mar 2016 11:06:13 +0000, ZombineDev wrote:
 The compiler should detect that this call is ambiguous and would not
 allow it.
It's a potentially significant amount of work to determine that the expression is ambiguous, or to disambiguate.
I don't think so. At least not much more than what currently the compiler does for overload resolution (comparing the void(A) overloard to the void(B)).
Currently, overload resolution deals with a similar problem with numeric literals. But there's a big difference between supporting it for one class of related types that's all known at compile time, and supporting it for an open-ended set of types that are entirely unrelated. If I were implement this, the first pass would require struct literal expressions to include the struct type. I might later consider whether to change that.
 Tuples literals / struct initializer literals /
 delegates should be classified before the end of the parsing phase
That is, you can determine whether something is a delegate literal or a struct literal (even without marking struct literals with their type) during parsing. For a moment, I misunderstood and thought you were talking about assigning types to them.
 (which is actually at least an order of magnitude faster than the
 semantic phase).
It wouldn't be parseable with an LL(k) parser, which is a minor thing. More to the point, depending on how DMD's parser is designed, it might be pretty awkward to parse something that might be a labeled statement or a struct variable initializer. If we can get something that's approximately as good (arguably better because it's less ambiguous, arguably worse because it's more to type) with far less work, then, given the small number of people who contribute to DMD, it would behoove us to choose the easier option.
Mar 10 2016
parent ZombineDev <petar.p.kirov gmail.com> writes:
On Friday, 11 March 2016 at 05:09:44 UTC, Chris Wright wrote:
 On Thu, 10 Mar 2016 19:01:53 +0000, ZombineDev wrote:

 On Monday, 7 March 2016 at 19:06:54 UTC, Chris Wright wrote:
 On Mon, 07 Mar 2016 11:06:13 +0000, ZombineDev wrote:
 The compiler should detect that this call is ambiguous and 
 would not allow it.
It's a potentially significant amount of work to determine that the expression is ambiguous, or to disambiguate.
I don't think so. At least not much more than what currently the compiler does for overload resolution (comparing the void(A) overloard to the void(B)).
Currently, overload resolution deals with a similar problem with numeric literals. But there's a big difference between supporting it for one class of related types that's all known at compile time, and supporting it for an open-ended set of types that are entirely unrelated.
It's not open-ended - size of the set is equal to the number of overloads taking structs, which in practice should be less than 20.
 If I were implement this, the first pass would require struct 
 literal expressions to include the struct type. I might later 
 consider whether to change that.
I'm OK with this. Even in the future we'll still need this, because with templates, type inference would not be possible.
 Tuples literals / struct initializer literals /
 delegates should be classified before the end of the parsing 
 phase
That is, you can determine whether something is a delegate literal or a struct literal (even without marking struct literals with their type) during parsing. For a moment, I misunderstood and thought you were talking about assigning types to them.
Yes, I meant just coarse-grained AST categorization, not type inference. We need this to be a simple process both ease the implementation, as well as the human reader. Ideally, the user should be able to tell the difference at a glance.
 (which is actually at least an order of magnitude faster than 
 the
 semantic phase).
It wouldn't be parseable with an LL(k) parser, which is a minor thing. More to the point, depending on how DMD's parser is designed, it might be pretty awkward to parse something that might be a labeled statement or a struct variable initializer.
Since DMD's parser is hand-written (and not generated from a grammer), I don't think it's design would be a problem. AFAIR, it uses arbitrary lookahead for a couple of things, because it is easier or faster.
 If we can get something that's approximately as good (arguably 
 better because it's less ambiguous, arguably worse because it's 
 more to type) with far less work, then, given the small number 
 of people who contribute to DMD, it would behoove us to choose 
 the easier option.
I don't think that the implementation would be a problem if we can convince Walter and Andrei. From what I've read in old bugzilla issues, they're both supportive of the idea of dedicated tuple syntax, however they wanted to hold from individual enhancements, in favour of a holistic design.
Mar 11 2016
prev sibling next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2016-03-06 18:35, Seb wrote:
 Hey all,

 I wanted to relive the discussion on named arguments and ping for its
 current status.

 There is a bunch of examples to show how needed a unified solution for
 this problem is, let me give you one from phobos [2].

 ```
 // I want to allow downsizing
 iota(10).sliced!(Yes.replaceArrayWithPointer, Yes.allowDownsize)(4);
 ```

 There is of course the alternative solution that an author overloads his
 function to the utmost, but this results in complexity and duplicated
 code (see e.g. redBlackTree in phobos [3]).

 Currently the best solution AFAICT is to use a struct to pass such
 flags, like

 ```
 struct Options{int x; int y=1; int z=2;}
 auto fun(Options options)
 {
      return options.x + options.y + options.z;
 }

 Options options = {x: 4, z: 3};
 auto a=fun(options);
 ```

 There are also other workarounds as discussed in [1] (e.g. with CTFE
 string analysis [4]).

 I general there two solutions to this problem
 1) get true named parameters support in D (probably complicated)
 2) allow struct inits in functions - e.g. fun({x: 4})

 For 2) Jacob Carlborg has proposed something similar three years ago. In
 his case he proposed anonymous structs which might be more generally
 applicable, however just created the struct seems easier and allows more
 It doesn't seem that complicated to me as the compiler already knows the
 type of the argument.

 Using structs is not ideal, because one can't require parameters, but
 this can be solved by having those parameters as normal ones like
 `sliced(4, {allowDownsize: true})` and it creates some maybe unnecessary
 overhead.
 However it is probably the easiest solution right now.

 What are your thoughts on this issue?
I think the simplest solution is to allow struct member initializer in all places a struct literal is accepted [1]. [1] https://issues.dlang.org/show_bug.cgi?id=15692 -- /Jacob Carlborg
Mar 06 2016
parent reply ZombineDev <petar.p.kirov gmail.com> writes:
On Sunday, 6 March 2016 at 20:35:49 UTC, Jacob Carlborg wrote:
 On 2016-03-06 18:35, Seb wrote:
 Hey all,

 I wanted to relive the discussion on named arguments and ping 
 for its
 current status.

 There is a bunch of examples to show how needed a unified 
 solution for
 this problem is, let me give you one from phobos [2].

 ```
 // I want to allow downsizing
 iota(10).sliced!(Yes.replaceArrayWithPointer, 
 Yes.allowDownsize)(4);
 ```

 There is of course the alternative solution that an author 
 overloads his
 function to the utmost, but this results in complexity and 
 duplicated
 code (see e.g. redBlackTree in phobos [3]).

 Currently the best solution AFAICT is to use a struct to pass 
 such
 flags, like

 ```
 struct Options{int x; int y=1; int z=2;}
 auto fun(Options options)
 {
      return options.x + options.y + options.z;
 }

 Options options = {x: 4, z: 3};
 auto a=fun(options);
 ```

 There are also other workarounds as discussed in [1] (e.g. 
 with CTFE
 string analysis [4]).

 I general there two solutions to this problem
 1) get true named parameters support in D (probably 
 complicated)
 2) allow struct inits in functions - e.g. fun({x: 4})

 For 2) Jacob Carlborg has proposed something similar three 
 years ago. In
 his case he proposed anonymous structs which might be more 
 generally
 applicable, however just created the struct seems easier and 
 allows more
 It doesn't seem that complicated to me as the compiler already 
 knows the
 type of the argument.

 Using structs is not ideal, because one can't require 
 parameters, but
 this can be solved by having those parameters as normal ones 
 like
 `sliced(4, {allowDownsize: true})` and it creates some maybe 
 unnecessary
 overhead.
 However it is probably the easiest solution right now.

 What are your thoughts on this issue?
I think the simplest solution is to allow struct member initializer in all places a struct literal is accepted [1]. [1] https://issues.dlang.org/show_bug.cgi?id=15692
+100 This can also pave the way for tuple literals, e.g.: // nothing new, ordinary struct member init Point2 point = { x: 3, y: 4 }; // auto variables are deduced to be tuples auto namedTuple = { x: 3, y: 4 }; // a.k.a anonymous classes in C# auto plainTuple = { 10, "hi", 3.14 }; // See also DIP32 Which I don't think will cause ambiguity with delegates: auto tuple3 = { getInt() }; tuple auto tuple3 = { getInt(); }; lambda // unambiguous (disallow delegates that contain only CommaExpr): auto tuple4 = { getInt(), getString(), getDouble() };
Mar 07 2016
parent reply Meta <jared771 gmail.com> writes:
On Monday, 7 March 2016 at 10:40:15 UTC, ZombineDev wrote:
 Which I don't think will cause ambiguity with delegates:
 auto tuple3 = { getInt() }; tuple
 auto tuple3 = { getInt(); }; lambda
Unfortunately this will not work. There was a tuple proposal sometime back in 2012 or 2013. I can't remember the exact reason, but somebody came up with a case where it can be ambiguous as to whether it's a tuple or a delegate.
Mar 07 2016
next sibling parent Marc =?UTF-8?B?U2Now7x0eg==?= <schuetzm gmx.net> writes:
On Monday, 7 March 2016 at 18:21:24 UTC, Meta wrote:
 On Monday, 7 March 2016 at 10:40:15 UTC, ZombineDev wrote:
 Which I don't think will cause ambiguity with delegates:
 auto tuple3 = { getInt() }; tuple
 auto tuple3 = { getInt(); }; lambda
Unfortunately this will not work. There was a tuple proposal sometime back in 2012 or 2013. I can't remember the exact reason, but somebody came up with a case where it can be ambiguous as to whether it's a tuple or a delegate.
I believe it was the empty tuple, which is indistinguishable from a do-nothing lambda. pragma(msg, typeof({})); // prints: // void function() pure nothrow nogc safe Nothing that can't be solved, I'd say. Just define `{}` to mean one of them and provide a workaround for the other one. OTOH, distinguishing between tuples and lambdas requires unlimited lookahead and backtracking, which can become expensive for pathological cases.
Mar 08 2016
prev sibling parent reply ZombineDev <petar.p.kirov gmail.com> writes:
On Monday, 7 March 2016 at 18:21:24 UTC, Meta wrote:
 On Monday, 7 March 2016 at 10:40:15 UTC, ZombineDev wrote:
 Which I don't think will cause ambiguity with delegates:
 auto tuple3 = { getInt() }; tuple
 auto tuple3 = { getInt(); }; lambda
Unfortunately this will not work. There was a tuple proposal sometime back in 2012 or 2013. I can't remember the exact reason, but somebody came up with a case where it can be ambiguous as to whether it's a tuple or a delegate.
Can you find the exact case? I'm really interested in getting proper tuple syntax support and I think that's important to cover all the possible cases before can agree on something and move towards implementing it.
Mar 10 2016
parent reply Meta <jared771 gmail.com> writes:
On Thursday, 10 March 2016 at 19:36:05 UTC, ZombineDev wrote:
 On Monday, 7 March 2016 at 18:21:24 UTC, Meta wrote:
 On Monday, 7 March 2016 at 10:40:15 UTC, ZombineDev wrote:
 Which I don't think will cause ambiguity with delegates:
 auto tuple3 = { getInt() }; tuple
 auto tuple3 = { getInt(); }; lambda
Unfortunately this will not work. There was a tuple proposal sometime back in 2012 or 2013. I can't remember the exact reason, but somebody came up with a case where it can be ambiguous as to whether it's a tuple or a delegate.
Can you find the exact case? I'm really interested in getting proper tuple syntax support and I think that's important to cover all the possible cases before can agree on something and move towards implementing it.
Here's the thread where it's pointed out: http://forum.dlang.org/post/mailman.372.1364547485.4724.digitalmars-d puremagic.com And here's Kenji's DIP: http://wiki.dlang.org/DIP32
Mar 11 2016
next sibling parent reply Marc =?UTF-8?B?U2Now7x0eg==?= <schuetzm gmx.net> writes:
On Friday, 11 March 2016 at 13:26:49 UTC, Meta wrote:
 On Thursday, 10 March 2016 at 19:36:05 UTC, ZombineDev wrote:
 On Monday, 7 March 2016 at 18:21:24 UTC, Meta wrote:
 Unfortunately this will not work. There was a tuple proposal 
 sometime back in 2012 or 2013. I can't remember the exact 
 reason, but somebody came up with a case where it can be 
 ambiguous as to whether it's a tuple or a delegate.
Can you find the exact case? I'm really interested in getting proper tuple syntax support and I think that's important to cover all the possible cases before can agree on something and move towards implementing it.
Here's the thread where it's pointed out: http://forum.dlang.org/post/mailman.372.1364547485.4724.digitalmars-d puremagic.com
Aside from the empty tuple, I can only find Timon's comment: http://forum.dlang.org/post/kj44fs$2iil$1 digitalmars.com
 0 "Inside tuple literal, ; never appears."
   {{;}}           // a tuple not matching your specification
   {{if(foo()){}}} // a non-tuple matching your specification
This is not a serious problem, because it just means that the DIP needs to be rephrased. There is no actual ambiguity.
 And here's Kenji's DIP:
 http://wiki.dlang.org/DIP32
Mar 11 2016
parent Chris Wright <dhasenan gmail.com> writes:
On Fri, 11 Mar 2016 14:29:39 +0000, Marc Sch├╝tz wrote:
 0 "Inside tuple literal, ; never appears."
   {{;}}           // a tuple not matching your specification
   {{if(foo()){}}} // a non-tuple matching your specification
This is not a serious problem, because it just means that the DIP needs to be rephrased. There is no actual ambiguity.
{{if(foo()){}}} looks like a tuple containing a delegate or a delegate containing a BlockStatement. {} looks like an empty tuple and an empty void delegate(). The proposal resolves the ambiguity by fiat: ambiguous things are classified as tuples. This is a breaking change and should have been called out in the proposal.
Mar 11 2016
prev sibling parent ZombineDev <petar.p.kirov gmail.com> writes:
On Friday, 11 March 2016 at 13:26:49 UTC, Meta wrote:
 On Thursday, 10 March 2016 at 19:36:05 UTC, ZombineDev wrote:
 On Monday, 7 March 2016 at 18:21:24 UTC, Meta wrote:
 [...]
Can you find the exact case? I'm really interested in getting proper tuple syntax support and I think that's important to cover all the possible cases before can agree on something and move towards implementing it.
Here's the thread where it's pointed out: http://forum.dlang.org/post/mailman.372.1364547485.4724.digitalmars-d puremagic.com And here's Kenji's DIP: http://wiki.dlang.org/DIP32
Thanks, I had read the DIP, but couldn't find the forum discussion. I added the link to the wiki page.
Mar 11 2016
prev sibling next sibling parent reply Martin Tschierschke <mt smartdolphin.de> writes:
On Sunday, 6 March 2016 at 17:35:38 UTC, Seb wrote:
 Hey all,

 I wanted to relive the discussion on named arguments and ping 
 for its current status.

 There is a bunch of examples to show how needed a unified 
 solution for this problem is, let me give you one from phobos 
 [2].

 ```
 // I want to allow downsizing
 iota(10).sliced!(Yes.replaceArrayWithPointer, 
 Yes.allowDownsize)(4);
 ```

 There is of course the alternative solution that an author 
 overloads his function to the utmost, but this results in 
 complexity and duplicated code (see e.g. redBlackTree in phobos 
 [3]).

 Currently the best solution AFAICT is to use a struct to pass 
 such flags, like

 ```
 struct Options{int x; int y=1; int z=2;}
 auto fun(Options options)
 {
     return options.x + options.y + options.z;
 }

 Options options = {x: 4, z: 3};
 auto a=fun(options);
 ```

 There are also other workarounds as discussed in [1] (e.g. with 
 CTFE string analysis [4]).

 I general there two solutions to this problem
 1) get true named parameters support in D (probably complicated)
 2) allow struct inits in functions - e.g. fun({x: 4})

 For 2) Jacob Carlborg has proposed something similar three 
 years ago. In his case he proposed anonymous structs which 
 might be more generally applicable, however just created the 
 struct seems easier and allows more
 It doesn't seem that complicated to me as the compiler already 
 knows the type of the argument.

 Using structs is not ideal, because one can't require 
 parameters, but this can be solved by having those parameters 
 as normal ones like `sliced(4, {allowDownsize: true})` and it 
 creates some maybe unnecessary overhead.
 However it is probably the easiest solution right now.

 What are your thoughts on this issue?

 On a side note: many templated functions are also complicated 
 and experiencing this issue, so it also might be worth to think 
 about this issue too ;-)

 Cheers,
What about this idea? A new word "as" or something similar. You have defined: int fun(int x=100, int y=100, int r=100){...} int xpos=100; int ypos=100; int radius=50; Now you are allowed to call: fun(xpos,ypos,radius); // normal nothing has changed! Call with "as": fun(ypos as y, xpos as x, radius as r); // different order! or fun(radius as r); // defaults values are used. <=> fun(,,radius); The compiler is looking for the right mapping?
Mar 08 2016
parent reply Chris Wright <dhasenan gmail.com> writes:
On Tue, 08 Mar 2016 13:52:09 +0000, Martin Tschierschke wrote:
 What about this idea? A new word "as" or something similar.
 fun(ypos as y, xpos as x, radius as r); // different order!
The syntax isn't an issue. There was one DIP about named parameters, but it was unpopular. It didn't change *anything* about overload resolution; it only had the compiler check that you provided arguments in the correct order, even if they were all of the same type. Even if there were a DIP everyone liked, nobody is signing up to implement it. DDMD is a little scary and not reflective of good design in D (having been translated by machine from C++). I might take a look, but I probably won't have the time to produce anything useful.
Mar 08 2016
parent reply Martin Tschierschke <mt smartdolphin.de> writes:
On Tuesday, 8 March 2016 at 18:46:02 UTC, Chris Wright wrote:
 On Tue, 08 Mar 2016 13:52:09 +0000, Martin Tschierschke wrote:
 What about this idea? A new word "as" or something similar. 
 fun(ypos as y, xpos as x, radius as r); // different order!
The syntax isn't an issue. There was one DIP about named parameters, but it was unpopular. It didn't change *anything* about overload resolution; it only had the compiler check that you provided arguments in the correct order, even if they were all of the same type. Even if there were a DIP everyone liked, nobody is signing up to implement it. DDMD is a little scary and not reflective of good design in D (having been translated by machine from C++). I might take a look, but I probably won't have the time to produce anything useful.
I have seen the "DIP about named parameters", and i liked the idea of getting a easier to read code, because it gets more verbose. My idea with the "val as x" was to avoid the need to define the functions in a different way. But as Idan Arye pointed out, it seems to be more difficult: "As far as I understand, the main two problems with named arguments are overloading ambiguity and the fact that argument names are not part of the signature." An other point on my wish list would be to allow string symbol notation like in ruby. Than using hashes (AA) for parameters gets more convenient: :symbol <= just short for => "symbol" h[:y]= 50; h[:x] = 100; // <=> h["y"] = 50; h["x"] = 100 For calling a function: auto params = [:y : 50, :x : 100] <=> auto params = ["y" : 50 , "x" : 100] Especially when the code is nested in a string for mixin purpose. (vibe.d templates). But maybe this crashes, because of the ambiguity if no space after a ":" is used in this place: auto hash = [ 1 :variable] meaning: auto hash = [ 1 : variable ] not auto hash = [1 "variable" ] which would make no sense either.
Mar 09 2016
parent reply Idan Arye <GenericNPC gmail.com> writes:
On Wednesday, 9 March 2016 at 10:06:25 UTC, Martin Tschierschke 
wrote:
 On Tuesday, 8 March 2016 at 18:46:02 UTC, Chris Wright wrote:
 On Tue, 08 Mar 2016 13:52:09 +0000, Martin Tschierschke wrote:
 What about this idea? A new word "as" or something similar. 
 fun(ypos as y, xpos as x, radius as r); // different order!
The syntax isn't an issue. There was one DIP about named parameters, but it was unpopular. It didn't change *anything* about overload resolution; it only had the compiler check that you provided arguments in the correct order, even if they were all of the same type. Even if there were a DIP everyone liked, nobody is signing up to implement it. DDMD is a little scary and not reflective of good design in D (having been translated by machine from C++). I might take a look, but I probably won't have the time to produce anything useful.
I have seen the "DIP about named parameters", and i liked the idea of getting a easier to read code, because it gets more verbose. My idea with the "val as x" was to avoid the need to define the functions in a different way. But as Idan Arye pointed out, it seems to be more difficult: "As far as I understand, the main two problems with named arguments are overloading ambiguity and the fact that argument names are not part of the signature." An other point on my wish list would be to allow string symbol notation like in ruby. Than using hashes (AA) for parameters gets more convenient: :symbol <= just short for => "symbol" h[:y]= 50; h[:x] = 100; // <=> h["y"] = 50; h["x"] = 100 For calling a function: auto params = [:y : 50, :x : 100] <=> auto params = ["y" : 50 , "x" : 100] Especially when the code is nested in a string for mixin purpose. (vibe.d templates). But maybe this crashes, because of the ambiguity if no space after a ":" is used in this place: auto hash = [ 1 :variable] meaning: auto hash = [ 1 : variable ] not auto hash = [1 "variable" ] which would make no sense either.
String symbols are Ruby's(and many Lisps', and maybe some other, less popular languages) way to do untyped enums and untyped structs. It's a dynamically typed languages thing and has no room in statically typed languages like D. D mimics dynamic typing to some extent by creating types on the fly with it's powerful templates mechanism - but a new type still needs to be created. D is geared toward reflection on types at compile time, not towards type detection at run-time... Allowing something like `auto params = [:y : 50, :x : 100]` won't really solve anything. It works nicely in Ruby, because Ruby has dynamic typing and with some syntactic sugar you get elegant syntax for dynamic structs. But in a structured language like D, you run into the problem that `[:y : 50, :x : 100] is an associative array with a determined type for it's values, so you can't do things like `[:y : 50, :x : "hello"]` - which greatly limits the usability of this syntax.
Mar 09 2016
next sibling parent reply Martin Tschierschke <mt smartdolphin.de> writes:
On Wednesday, 9 March 2016 at 12:55:16 UTC, Idan Arye wrote:
[...]
 An other point on my wish list would be to allow string symbol 
 notation
 like in ruby. Than using hashes (AA) for parameters gets more 
 convenient:

 :symbol <= just short for => "symbol"

 h[:y]= 50; h[:x] = 100; // <=> h["y"] = 50; h["x"] = 100
 String symbols are Ruby's(and many Lisps', and maybe some 
 other, less popular languages) way to do untyped enums and 
 untyped structs. It's a dynamically typed languages thing and 
 has no room in statically typed languages like D. D mimics 
 dynamic typing to some extent by creating types on the fly with 
 it's powerful templates mechanism - but a new type still needs 
 to be created. D is geared toward reflection on types at 
 compile time, not towards type detection at run-time...
Thats true.
 Allowing something like `auto params = [:y : 50, :x : 100]` 
 won't really solve anything. It works nicely in Ruby, because 
 Ruby has dynamic typing and with some syntactic sugar you get 
 elegant syntax for dynamic structs. But in a structured 
 language like D, you run into the problem that `[:y : 50, :x : 
 100] is an associative array with a determined type for it's 
 values, so you can't do things like `[:y : 50, :x : "hello"]` - 
 which greatly limits the usability of this syntax.
Yes.Ok. What I like about the :symbol notation is, that a string witch is used only to distinguish between different objects in an Hash / AA has a complete different purpose than a string used to be displayed for the user. I think that writeln("Name", a[:name]); is easier to read, than writeln("Name", a["name"]); especially if the structures are getting bigger, or you are in a vibe.d jade template string where you would have to use additional quoting to write: a(href="a[\"url\"]") a["link_text"] a(href="a[:url]") a[:link_text] May be I should get rid of this by using a struct for my mysql results to display? (=> a.url and a.link_text ) Just my 2 Cents :-)
Mar 09 2016
parent Idan Arye <GenericNPC gmail.com> writes:
On Wednesday, 9 March 2016 at 13:39:57 UTC, Martin Tschierschke 
wrote:
 On Wednesday, 9 March 2016 at 12:55:16 UTC, Idan Arye wrote:
 [...]
 [...]
 [...]
Thats true.
 [...]
Yes.Ok. What I like about the :symbol notation is, that a string witch is used only to distinguish between different objects in an Hash / AA has a complete different purpose than a string used to be displayed for the user. I think that writeln("Name", a[:name]); is easier to read, than writeln("Name", a["name"]); especially if the structures are getting bigger, or you are in a vibe.d jade template string where you would have to use additional quoting to write: a(href="a[\"url\"]") a["link_text"] a(href="a[:url]") a[:link_text] May be I should get rid of this by using a struct for my mysql results to display? (=> a.url and a.link_text ) Just my 2 Cents :-)
If nested strings is what's bothering you, you can always use backticks. Or opDispatch(though I don't recommend it, as it tends to screw up compilation errors). But these won't let you have fields with different types, and since Voldemort types are so easy in D you are probably better off with structs.
Mar 09 2016
prev sibling parent reply Michael Coulombe <kirsybuu gmail.com> writes:
On Wednesday, 9 March 2016 at 12:55:16 UTC, Idan Arye wrote:
 Allowing something like `auto params = [:y : 50, :x : 100]` 
 won't really solve anything. It works nicely in Ruby, because 
 Ruby has dynamic typing and with some syntactic sugar you get 
 elegant syntax for dynamic structs. But in a structured 
 language like D, you run into the problem that `[:y : 50, :x : 
 100] is an associative array with a determined type for it's 
 values, so you can't do things like `[:y : 50, :x : "hello"]` - 
 which greatly limits the usability of this syntax.
I posted (in a previous thread about this issue) a library solution to this: import std.typecons, std.meta; private struct Named(string n, T) { enum name = n; T value; } template NamedToAS(N...) { static if (N.length == 0) { alias NamedToAS = AliasSeq!(); } else { alias NamedToAS = AliasSeq!(typeof(N[0].init.value), N[0].name, NamedToAS!(N[1 .. $])); } } struct Tup { static auto opDollar(size_t i)() { return immutable KeyWordDollar(); } static auto opIndex(NamedArgs...)(NamedArgs nas) { Tuple!(NamedToAS!NamedArgs) tup; foreach(i, ref na ; tup) { na = nas[i].value; } return tup; } } void overrideFrom(TupDef, TupOver)(ref TupDef defaultMap, ref TupOver overrideMap) { foreach(i, ref field ; overrideMap) { __traits(getMember, defaultMap, TupOver.fieldNames[i]) = field; } } auto println(KWA)(KWA kwa) { auto args = Tup[$.str = "abc", $.repeat = 2, $.sep = " "]; args.overrideFrom(kwa); import std.stdio; bool first = true; foreach(i ; 0 .. args.repeat) { if (first) first = false; else write(args.sep); write(args.str); } writeln(); } unittest { println(Tup[$.str = "xyz", $.repeat = 8]); }
Mar 09 2016
parent Michael Coulombe <kirsybuu gmail.com> writes:
On Thursday, 10 March 2016 at 05:17:03 UTC, Michael Coulombe 
wrote:
 [...]
Whoops, forgot this: private immutable struct KeyWordDollar { property auto opDispatch(string param, T)(T t) { return Named!(param,T)(t); } }
Mar 09 2016
prev sibling next sibling parent arturg <var.spool.mail700 gmail.com> writes:
hi, has the UDA syntax been proposed already?

void fun(int foo, int bar, int bazDaz = 0){}

fun(10, 30, 50); // normal
fun(10, 30); normal with default

fun(10, 30,  bazDaz 50); // mixed
fun( bazDaz 50, 10, 30) // mixed and reordered
fun( bar 30, 10); // mixed, reordered and default

fun( foo 10,  bar 30,  bazDaz 50); // named
fun( bar 30,  bazDaz 50,  foo 10); // named and reordered

 foo int a = 10;
 bar int b = 30;
 bazDaz int c = 50;

fun(c, a, b); // hm allowed or not?

but if built in tuples could be used to do named args then it 
might be better to have those.
Mar 08 2016
prev sibling next sibling parent reply Idan Arye <GenericNPC gmail.com> writes:
On Sunday, 6 March 2016 at 17:35:38 UTC, Seb wrote:
 Hey all,

 I wanted to relive the discussion on named arguments and ping 
 for its current status.

 There is a bunch of examples to show how needed a unified 
 solution for this problem is, let me give you one from phobos 
 [2].

 ```
 // I want to allow downsizing
 iota(10).sliced!(Yes.replaceArrayWithPointer, 
 Yes.allowDownsize)(4);
 ```

 There is of course the alternative solution that an author 
 overloads his function to the utmost, but this results in 
 complexity and duplicated code (see e.g. redBlackTree in phobos 
 [3]).

 Currently the best solution AFAICT is to use a struct to pass 
 such flags, like

 ```
 struct Options{int x; int y=1; int z=2;}
 auto fun(Options options)
 {
     return options.x + options.y + options.z;
 }

 Options options = {x: 4, z: 3};
 auto a=fun(options);
 ```

 There are also other workarounds as discussed in [1] (e.g. with 
 CTFE string analysis [4]).

 I general there two solutions to this problem
 1) get true named parameters support in D (probably complicated)
 2) allow struct inits in functions - e.g. fun({x: 4})

 For 2) Jacob Carlborg has proposed something similar three 
 years ago. In his case he proposed anonymous structs which 
 might be more generally applicable, however just created the 
 struct seems easier and allows more
 It doesn't seem that complicated to me as the compiler already 
 knows the type of the argument.

 Using structs is not ideal, because one can't require 
 parameters, but this can be solved by having those parameters 
 as normal ones like `sliced(4, {allowDownsize: true})` and it 
 creates some maybe unnecessary overhead.
 However it is probably the easiest solution right now.

 What are your thoughts on this issue?

 On a side note: many templated functions are also complicated 
 and experiencing this issue, so it also might be worth to think 
 about this issue too ;-)

 Cheers,

 Seb

 [1] 
 http://forum.dlang.org/post/pxndhoskpjxvnoacajaz forum.dlang.org
 [2] https://github.com/DlangScience/mir/issues/18
 [3] 
 https://github.com/D-Programming-Language/phobos/pull/4041/files
 [4] 
 https://github.com/timotheecour/dtools/blob/master/dtools/util/functional.d
As far as I understand, the main two problems with named arguments are overloading ambiguity and the fact that argument names are not part of the signature. Using a struct to define the named arguments solves the signature problem, but the ambiguity problem remains - it simply gets shifted from "which overload to choose" to "which struct to choose". The consensus here seems to be that ambiguous code should be result in compilation errors(and that it should be easy for the compiler to detect that the code is ambiguous!), and that if structs are used to declare named arguments, then solving overloading ambiguities should be done by constructing the struct explicitly. Then everyone disagree regarding how structs should be created explicitly with named fields. I would like to suggest an idea that will allow solving ambiguities without the need for new syntax(behind the syntax for declaring and using named arguments), be consistent with existing D features, and as a bonus provide a much nicer syntax: Make it like variadic functions! Declaring the named arguments variadically will be done by adding `...` after a struct argument: struct Options{int x; int y=1; int z=2;} auto fun(Options options ...) We'll need a syntax for specifying the arguments - but that's more of a matter of taste than an actual technical problem, and it's going to be bikeshedded over and over, so for the purpose of describing my idea let's pick a Ruby-style `:`(because `=` will break the rule of if-it-compiles-as-C-it-should-work-like-C): fun(x: 4, z: 3); I've promised you to solve ambiguity, right? Well, just like regular variadic functions can be called with an explicit array, named arguments variadic functions should also be callable with an explicit struct: Options options = {x: 4, z: 3}; fun(options); Which brings us back to square one, right? Wrong! Because at this point, it should be trivial to give structs a second default constructor: this(typeof(this) that ...) { this = that; } (actual implementation may be more efficient and robust(preferably a syntactic sugar for postblit), but you get the idea). So now structs can be constructed with named arguments, and ambiguities could be neatly solved with: fun(Options(x: 4, z: 3)); P.S: If a struct-based solution is ever implemented for named arguments, I do hope it'll support template inference, where a simple PODS(Plain Old D Struct... sorry...) is automatically created based on the supplied arguments' names and types(and order!). UDA support for that could be nice too...
Mar 08 2016
parent Marc =?UTF-8?B?U2Now7x0eg==?= <schuetzm gmx.net> writes:
On Tuesday, 8 March 2016 at 20:32:02 UTC, Idan Arye wrote:
 Declaring the named arguments variadically will be done by 
 adding `...` after a struct argument:

     struct Options{int x; int y=1; int z=2;}
     auto fun(Options options ...)

 We'll need a syntax for specifying the arguments - but that's 
 more of a matter of taste than an actual technical problem, and 
 it's going to be bikeshedded over and over, so for the purpose 
 of describing my idea let's pick a Ruby-style `:`(because `=` 
 will break the rule of 
 if-it-compiles-as-C-it-should-work-like-C):

     fun(x: 4, z: 3);

 I've promised you to solve ambiguity, right?
--snip-- I'm not sure, but I think the problem Walter has lies with _detecting_ ambiguity in the first place, because that would make overload resolution more complicated. I personally don't think it's that big a problem, because selecting the candidates could be a step before actual (= as it is now) overload resolution.
Mar 10 2016
prev sibling parent reply Edwin van Leeuwen <edder tkwsping.nl> writes:
On Sunday, 6 March 2016 at 17:35:38 UTC, Seb wrote:
 Hey all,

 Using structs is not ideal, because one can't require 
 parameters, but this can be solved by having those parameters 
 as normal ones like `sliced(4, {allowDownsize: true})` and it 
 creates some maybe unnecessary overhead.
 However it is probably the easiest solution right now.
In ggplotd I often use named tuples as and "anonymoous" struct: Tuple!(double,"x")( 0.0 ) I also added a merge function that will return a tuple containing merged named tuples: Tuple!(double,"x",string,"colour")(-1, "black").merge(Tuple!(double,"x")(0.0)) returns: Tuple!(double,"x",string,"colour")(0, "black"); As an aside the merge function also works with structs so you can do the following: struct Point { double x; double y; } Tuple!(double,"x",string,"colour")(-1, "black").merge(Point(1,2)) returns: Tuple!(double,"x",double,"y",string,"colour")(1, 2, "black"); It works reasonably well, except that the tuples require a lot of typing.
Mar 08 2016
next sibling parent Edwin van Leeuwen <edder tkwsping.nl> writes:
On Wednesday, 9 March 2016 at 07:30:31 UTC, Edwin van Leeuwen 
wrote:
 I also added a merge function that will return a tuple 
 containing merged named tuples:

 Tuple!(double,"x",string,"colour")(-1, 
 "black").merge(Tuple!(double,"x")(0.0))

 returns:
 Tuple!(double,"x",string,"colour")(0, "black");
Quick clarification. The merge function is used to merge the passed tuple, with the default values needed in the function. This way we don't have to worry about missing values.
Mar 08 2016
prev sibling parent John Colvin <john.loughran.colvin gmail.com> writes:
On Wednesday, 9 March 2016 at 07:30:31 UTC, Edwin van Leeuwen 
wrote:
 On Sunday, 6 March 2016 at 17:35:38 UTC, Seb wrote:
 [...]
In ggplotd I often use named tuples as and "anonymoous" struct: Tuple!(double,"x")( 0.0 ) I also added a merge function that will return a tuple containing merged named tuples: Tuple!(double,"x",string,"colour")(-1, "black").merge(Tuple!(double,"x")(0.0)) returns: Tuple!(double,"x",string,"colour")(0, "black"); As an aside the merge function also works with structs so you can do the following: struct Point { double x; double y; } Tuple!(double,"x",string,"colour")(-1, "black").merge(Point(1,2)) returns: Tuple!(double,"x",double,"y",string,"colour")(1, 2, "black"); It works reasonably well, except that the tuples require a lot of typing.
slightly tangentially, you might be interested in this: https://github.com/D-Programming-Language/phobos/pull/4043
Mar 09 2016