digitalmars.D.announce - foo => "bar" key/value literals in D!
- Adam D. Ruppe (59/59) May 23 2016 Have I gone completely mad?!?!
- Daniel N (9/10) May 23 2016 That makes two of us, I also use similar techniques.
- Adam D. Ruppe (14/18) May 23 2016 Soooo I'm actually not entirely convinced on that change.
- Daniel N (16/24) May 25 2016 From an end-user perspective I find it reasonable to expect that
- Jacob Carlborg (10/25) May 25 2016 I don't think this is the right approach. I think the correct approach
- Daniel N (7/12) May 25 2016 Issue9608 is good too, but insufficient.
- Jacob Carlborg (5/11) May 25 2016 Hmm, I though the second case was already possible, but I see now that
- Meta (15/19) May 25 2016 Is that true? I don't claim to know exactly how the compiler
- Jacob Carlborg (4/17) May 26 2016 Well, a runtime parameter in a template then :)
- Adam D. Ruppe (13/19) May 25 2016 Those are indeed both can work to fetch names, though they aren't
- Meta (11/70) May 23 2016 Clever and terrible. Now just modify the code to generate a
- Meta (3/13) May 23 2016 And looks like I filed a bug 2 years ago and then promptly forgot
- Adam D. Ruppe (7/12) May 23 2016 Right, I tried that too and it didn't work... and I think it
- Bauss (3/14) May 23 2016 This is a pretty amazing find!
- Jacob Carlborg (35/81) May 23 2016 __parameters doesn't work because an "untyped" lambda is a template and
- Andrei Alexandrescu (6/7) May 24 2016 This is very creative, thanks. Definitely make it part of TWID :o).
- Adam D. Ruppe (8/10) May 25 2016 Oh, that's the buggy area. The compiler keeps parameter names for
- Vladimir Panteleev (5/6) May 27 2016 Yes, though what does it have to do with this thread? :D
- Taylor Hillegeist (2/8) May 27 2016 This is very nice... way more clean than I imagined possible.
- Andrei Alexandrescu (2/13) May 30 2016 s/args/make/g and we have a nice function for std.conv. -- Andrei
- Vladimir Panteleev (4/21) Jun 10 2016 Do I gather from that that you propose adding only the overload
- Andrei Alexandrescu (9/27) Jun 11 2016 No, both are nice to have. If one name is needed for both, "args" is
- ArturG (11/20) Jun 11 2016 would'nt it be possible to take the type as a runtime param
- Vladimir Panteleev (3/21) Jun 11 2016 Taking an address creates a function pointer, which loses the
- ArturG (4/6) Jun 11 2016 unfortunatly yes, but it works as a struct or class initializer
- John (26/29) Jun 08 2016 Another cool thing this enables: object initializers.
- Vladimir Panteleev (2/9) Jun 10 2016 Already in the link you quoted, FYI.
- Era Scarecrow (2/17) May 31 2016 Pretty snazzy :) Like enums, except not...
Have I gone completely mad?!?! --- void main() { import std.stdio; writeln(obj!( foo => "bar", baz => 12 )); } --- Prints out: { foo: bar baz: 12 } A few tweaks would make a whole loose typed hash thing more akin to Ruby or PHP than D. What's obj? Behold: string obj(T...)() { import std.conv, std.traits; string jsonResult = "{"; foreach(arg; T) { jsonResult ~= "\n\t"; // I don't know why the usual is(__parameters) trick // won't work here, but a stringof hack will! string hack = typeof(arg!string).stringof; import std.string; hack = hack[hack.indexOf("function(string ") + "function(string ".length .. $]; hack = hack[0 .. hack.indexOf(")")]; jsonResult ~= hack; jsonResult ~= ": "; jsonResult ~= to!string(arg("")); } jsonResult ~= "\n}"; return jsonResult; } As you probably know, D has a couple lambda literal syntaxes. One of these is the fat arrow, with a valid form of argument => return_expression. The compiler makes templates out of these when you pass them around.... and those templates contain the parameters, including the name, and are callable code (if instantiated with a concrete type). I was disappointed to see the ordinary reflection tools didn't work here - I know, I'm abusing the language - but the trusty old .stringof hack did! Combined with the magic knowledge that these things are templates, I instantiated them (tbh I was a bit surprised it actually let me!) and extracted the name of the argument. Then simply running it results in the value at runtime. Combining these with facilities for building values - here, I just did a string but it could be whatever - results in an array that almost looks like it was pulled from one of those dynamic languages. Complete program here (may include bug fixes made after posting this announcement): http://arsdnet.net/dcode/have_i_lost_my_marbles.d I might actually use this nasty trick in some of my ugly code.
May 23 2016
On Monday, 23 May 2016 at 19:00:40 UTC, Adam D. Ruppe wrote:Have I gone completely mad?!?!That makes two of us, I also use similar techniques. Please help argue in favour of pulling this: https://github.com/dlang/phobos/pull/3620/files https://issues.dlang.org/show_bug.cgi?id=13780 This pull request just removes an intentional restriction and make this feature more easily accessible for meta-programming, so that not everyone has to reinvent the wheel in their own libraries.
May 23 2016
On Monday, 23 May 2016 at 20:08:11 UTC, Daniel N wrote:This pull request just removes an intentional restriction and make this feature more easily accessible for meta-programming, so that not everyone has to reinvent the wheel in their own libraries.Soooo I'm actually not entirely convinced on that change. Parameter names should *not* be part of a function pointer or delegate type - that's not supported and kinda randomly buggy to depend on (if the types match, the names might not: the compiler is free to reuse the structure for matching types which means the names you get might be the ones from a previous definition!) Alias arguments are IMO different though, because they aren't a function pointer/delegate type, they are actually a function symbol, just one that happens to be defined inline. They should work. But I think the restriction on delegates' parameter names is actually a good one, protecting you from bugs in more complex programs.
May 23 2016
On Tuesday, 24 May 2016 at 01:33:40 UTC, Adam D. Ruppe wrote:On Monday, 23 May 2016 at 20:08:11 UTC, Daniel N wrote:From an end-user perspective I find it reasonable to expect that an API which takes lambda:s works consistently for both below examples. i.e. if we support one we should support the other. [1] fun!( x => y) [2] fun!((int x) => y) Currently I just copy/paste the fix to ParameterIdentifierTuple everywhere as the needed primitives already exist. Removing the already existing primitives now is imho too late, as it breaks code, which sometimes is OK, but this time there's no clear solution how to rewrite the broken code. Unfinished Library based Named-parameters proof-of-concept: (yes, I know. It needs to be updated to handle case [2]) https://dpaste.dzfl.pl/ae55de677f86 /vote open floodgates for introspection, capitalizing on the strengths of D.This pull request just removes an intentional restriction and make this feature more easily accessible for meta-programming, so that not everyone has to reinvent the wheel in their own libraries.Soooo I'm actually not entirely convinced on that change. Parameter names should *not* be part of a function pointer or delegate type
May 25 2016
On 2016-05-25 11:24, Daniel N wrote:From an end-user perspective I find it reasonable to expect that an API which takes lambda:s works consistently for both below examples. i.e. if we support one we should support the other. [1] fun!( x => y) [2] fun!((int x) => y) Currently I just copy/paste the fix to ParameterIdentifierTuple everywhere as the needed primitives already exist. Removing the already existing primitives now is imho too late, as it breaks code, which sometimes is OK, but this time there's no clear solution how to rewrite the broken code. Unfinished Library based Named-parameters proof-of-concept: (yes, I know. It needs to be updated to handle case [2]) https://dpaste.dzfl.pl/ae55de677f86 /vote open floodgates for introspection, capitalizing on the strengths of D.I don't think this is the right approach. I think the correct approach is to allow introspection of template parameters [1]. That would allow to get the parameter names without having to instantiate the template. ParameterIdentifierTuple can hopefully be updated to take advantage of this feature when it's available in compiler without requiring to change the API of ParameterIdentifierTuple. [1] https://github.com/dlang/dmd/pull/5201 -- /Jacob Carlborg
May 25 2016
On Wednesday, 25 May 2016 at 11:22:41 UTC, Jacob Carlborg wrote:On 2016-05-25 11:24, Daniel N wrote: ParameterIdentifierTuple can hopefully be updated to take advantage of this feature when it's available in compiler without requiring to change the API of ParameterIdentifierTuple. [1] https://github.com/dlang/dmd/pull/5201Issue9608 is good too, but insufficient. import std.traits; static assert(__traits(isTemplate, a => 0)); static assert(!__traits(isTemplate, (int a) => 0)); The 2nd case can't be solved by 9608 since it's about introspecting templates and "(int a) => 0)" is no template.
May 25 2016
On 2016-05-25 13:57, Daniel N wrote:Issue9608 is good too, but insufficient. import std.traits; static assert(__traits(isTemplate, a => 0)); static assert(!__traits(isTemplate, (int a) => 0)); The 2nd case can't be solved by 9608 since it's about introspecting templates and "(int a) => 0)" is no template.Hmm, I though the second case was already possible, but I see now that it isn't. -- /Jacob Carlborg
May 25 2016
On Wednesday, 25 May 2016 at 11:22:41 UTC, Jacob Carlborg wrote:I don't think this is the right approach. I think the correct approach is to allow introspection of template parameters [1]. That would allow to get the parameter names without having to instantiate the template.Is that true? I don't claim to know exactly how the compiler works, but given a template lambda: foo => 3 Doesn't it generate something like the following? template __lambda10293(T) { <deduced return type> __lambda10293(T foo) { return 3; } } In that case, "foo" is not a template parameter, but a runtime parameter, so template parameter introspection would not help in this case.
May 25 2016
On 2016-05-25 20:55, Meta wrote:Is that true? I don't claim to know exactly how the compiler works, but given a template lambda: foo => 3 Doesn't it generate something like the following? template __lambda10293(T) { <deduced return type> __lambda10293(T foo) { return 3; } } In that case, "foo" is not a template parameter, but a runtime parameter, so template parameter introspection would not help in this case.Well, a runtime parameter in a template then :) -- /Jacob Carlborg
May 26 2016
On Wednesday, 25 May 2016 at 09:24:31 UTC, Daniel N wrote:From an end-user perspective I find it reasonable to expect that an API which takes lambda:s works consistently for both below examples. i.e. if we support one we should support the other. [1] fun!( x => y) [2] fun!((int x) => y)Those are indeed both can work to fetch names, though they aren't exactly the same since the first one is a template and the second one isn't. The technique is slightly different right now, but both doable (and I have no objection to unifying the techniques) The big difference I'm talking about is passing them as an alias argument vs as a runtime argument. Runtime function pointers lose many of the introspection capabilities of compile time alias arguments (well, runtime args still can pretend to give you names, but you'll find it doesn't actually work very well). From the looks of the Phobos PR too, it looks like some of the commentators conflated the two issues too: template vs non-template isn't the same as alias vs delegate.
May 25 2016
On Monday, 23 May 2016 at 19:00:40 UTC, Adam D. Ruppe wrote:Have I gone completely mad?!?! --- void main() { import std.stdio; writeln(obj!( foo => "bar", baz => 12 )); } --- Prints out: { foo: bar baz: 12 } A few tweaks would make a whole loose typed hash thing more akin to Ruby or PHP than D. What's obj? Behold: string obj(T...)() { import std.conv, std.traits; string jsonResult = "{"; foreach(arg; T) { jsonResult ~= "\n\t"; // I don't know why the usual is(__parameters) trick // won't work here, but a stringof hack will! string hack = typeof(arg!string).stringof; import std.string; hack = hack[hack.indexOf("function(string ") + "function(string ".length .. $]; hack = hack[0 .. hack.indexOf(")")]; jsonResult ~= hack; jsonResult ~= ": "; jsonResult ~= to!string(arg("")); } jsonResult ~= "\n}"; return jsonResult; } As you probably know, D has a couple lambda literal syntaxes. One of these is the fat arrow, with a valid form of argument => return_expression. The compiler makes templates out of these when you pass them around.... and those templates contain the parameters, including the name, and are callable code (if instantiated with a concrete type). I was disappointed to see the ordinary reflection tools didn't work here - I know, I'm abusing the language - but the trusty old .stringof hack did! Combined with the magic knowledge that these things are templates, I instantiated them (tbh I was a bit surprised it actually let me!) and extracted the name of the argument. Then simply running it results in the value at runtime. Combining these with facilities for building values - here, I just did a string but it could be whatever - results in an array that almost looks like it was pulled from one of those dynamic languages. Complete program here (may include bug fixes made after posting this announcement): http://arsdnet.net/dcode/have_i_lost_my_marbles.d I might actually use this nasty trick in some of my ugly code.Clever and terrible. Now just modify the code to generate a struct or class and you've invented new anonymous struct/object syntax. Also, I think this has revealed a bug (or deficiency) in the compiler. If you put this inside the foreach loop: import std.traits; alias inst = arg!string; pragma(msg, ParameterIdentifierTuple!inst); It prints out `tuple("")` both times, meaning that for some reason it sees these lambdas as having no parameter names.
May 23 2016
On Tuesday, 24 May 2016 at 01:11:39 UTC, Meta wrote:Clever and terrible. Now just modify the code to generate a struct or class and you've invented new anonymous struct/object syntax. Also, I think this has revealed a bug (or deficiency) in the compiler. If you put this inside the foreach loop: import std.traits; alias inst = arg!string; pragma(msg, ParameterIdentifierTuple!inst); It prints out `tuple("")` both times, meaning that for some reason it sees these lambdas as having no parameter names.And looks like I filed a bug 2 years ago and then promptly forgot about it.
May 23 2016
On Tuesday, 24 May 2016 at 01:11:39 UTC, Meta wrote:Clever and terrible. Now just modify the code to generate a struct or class and you've invented new anonymous struct/object syntax.Indeed.Also, I think this has revealed a bug (or deficiency) in the compiler. If you put this inside the foreach loop:Right, I tried that too and it didn't work... and I think it should, since this is an alias argument instead of a runtime function pointer, the names should be available. I tried the low level is(foo params == __parameters) too to no avail, the compiler is the buggy one here rather than Phobos.
May 23 2016
On Monday, 23 May 2016 at 19:00:40 UTC, Adam D. Ruppe wrote:Have I gone completely mad?!?! --- void main() { import std.stdio; writeln(obj!( foo => "bar", baz => 12 )); } --- [...]This is a pretty amazing find!
May 23 2016
On 2016-05-23 21:00, Adam D. Ruppe wrote:Have I gone completely mad?!?! --- void main() { import std.stdio; writeln(obj!( foo => "bar", baz => 12 )); } --- Prints out: { foo: bar baz: 12 } A few tweaks would make a whole loose typed hash thing more akin to Ruby or PHP than D. What's obj? Behold: string obj(T...)() { import std.conv, std.traits; string jsonResult = "{"; foreach(arg; T) { jsonResult ~= "\n\t"; // I don't know why the usual is(__parameters) trick // won't work here, but a stringof hack will! string hack = typeof(arg!string).stringof; import std.string; hack = hack[hack.indexOf("function(string ") + "function(string ".length .. $]; hack = hack[0 .. hack.indexOf(")")]; jsonResult ~= hack; jsonResult ~= ": "; jsonResult ~= to!string(arg("")); } jsonResult ~= "\n}"; return jsonResult; }That's pretty cool and pretty ugly :).As you probably know, D has a couple lambda literal syntaxes. One of these is the fat arrow, with a valid form of argument => return_expression. The compiler makes templates out of these when you pass them around.... and those templates contain the parameters, including the name, and are callable code (if instantiated with a concrete type). I was disappointed to see the ordinary reflection tools didn't work here - I know, I'm abusing the language - but the trusty old .stringof hack did! Combined with the magic knowledge that these things are templates, I instantiated them (tbh I was a bit surprised it actually let me!) and extracted the name of the argument.__parameters doesn't work because an "untyped" lambda is a template and __parameters works with functions, but I guess you already know that. Instantiating the lambda and then using __parameters should work. There's a PR for DMD which adds support for inspecting template parameters [1] that would help. Unfortunately it's closed. Here's a version building an associative array mapping strings to variants: import std.stdio : println = writeln; import std.variant; Variant[string] hash(T...)() { import std.conv, std.traits; Variant[string] aa; foreach(arg; T) { // I don't know why the usual is(__parameters) trick // won't work here, but a stringof hack will! string hack = typeof(arg!string).stringof; import std.string; hack = hack[hack.indexOf("function(string ") + "function(string ".length .. $]; hack = hack[0 .. hack.indexOf(")")]; aa[hack] = Variant(arg("")); } return aa; } void main() { auto h = hash!( foo => 3, bar => "asd" ); writeln(h); } [1] https://github.com/dlang/dmd/pull/5201 -- /Jacob Carlborg
May 23 2016
On 5/23/16 3:00 PM, Adam D. Ruppe wrote:Have I gone completely mad?!?!This is very creative, thanks. Definitely make it part of TWID :o). Apparently it can be made to work with non-templates as well, see https://dpaste.dzfl.pl/c4b7a8b6978b. Regarding applying a hack/ingenious label, I'd cautiously say "looks interesting" and wait for a bit of experience to accumulate. -- Andrei
May 24 2016
On Tuesday, 24 May 2016 at 16:00:32 UTC, Andrei Alexandrescu wrote:Apparently it can be made to work with non-templates as well, see https://dpaste.dzfl.pl/c4b7a8b6978b.Oh, that's the buggy area. The compiler keeps parameter names for runtime delegates... but it also reuses the structures. Thus, in simple examples, it works, but outside that it becomes very unreliable. Parameter names are NOT part of a delegate's type. But an alias parameter to a template is a different story!
May 25 2016
On Monday, 23 May 2016 at 19:00:40 UTC, Adam D. Ruppe wrote:Have I gone completely mad?!?!Yes, though what does it have to do with this thread? :D This is by far the most appealing way to implement named arguments that I've seen so far: https://github.com/CyberShadow/ae/blob/master/utils/meta/args.d
May 27 2016
On Friday, 27 May 2016 at 18:10:59 UTC, Vladimir Panteleev wrote:On Monday, 23 May 2016 at 19:00:40 UTC, Adam D. Ruppe wrote:This is very nice... way more clean than I imagined possible.Have I gone completely mad?!?!Yes, though what does it have to do with this thread? :D This is by far the most appealing way to implement named arguments that I've seen so far: https://github.com/CyberShadow/ae/blob/master/utils/meta/args.d
May 27 2016
On 5/27/16 10:17 PM, Taylor Hillegeist wrote:On Friday, 27 May 2016 at 18:10:59 UTC, Vladimir Panteleev wrote:s/args/make/g and we have a nice function for std.conv. -- AndreiOn Monday, 23 May 2016 at 19:00:40 UTC, Adam D. Ruppe wrote:This is very nice... way more clean than I imagined possible.Have I gone completely mad?!?!Yes, though what does it have to do with this thread? :D This is by far the most appealing way to implement named arguments that I've seen so far: https://github.com/CyberShadow/ae/blob/master/utils/meta/args.d
May 30 2016
On Tuesday, 31 May 2016 at 06:21:03 UTC, Andrei Alexandrescu wrote:On 5/27/16 10:17 PM, Taylor Hillegeist wrote:Do I gather from that that you propose adding only the overload that creates structs?On Friday, 27 May 2016 at 18:10:59 UTC, Vladimir Panteleev wrote:s/args/make/g and we have a nice function for std.conv. -- AndreiOn Monday, 23 May 2016 at 19:00:40 UTC, Adam D. Ruppe wrote:This is very nice... way more clean than I imagined possible.Have I gone completely mad?!?!Yes, though what does it have to do with this thread? :D This is by far the most appealing way to implement named arguments that I've seen so far: https://github.com/CyberShadow/ae/blob/master/utils/meta/args.d
Jun 10 2016
On 6/11/16 3:57 AM, Vladimir Panteleev wrote:On Tuesday, 31 May 2016 at 06:21:03 UTC, Andrei Alexandrescu wrote:No, both are nice to have. If one name is needed for both, "args" is indeed a good commonality. "Invoke function f with these args" and "Construct an object of type T with these args". The problem is it's not very intuitive in usage. Perhaps "call" for functions and "make" for objects are better. If we add these to std.experimental.functional and std.experimental.conv and they get a lot of usage we might make a small change to the language. AndreiOn 5/27/16 10:17 PM, Taylor Hillegeist wrote:Do I gather from that that you propose adding only the overload that creates structs?On Friday, 27 May 2016 at 18:10:59 UTC, Vladimir Panteleev wrote:s/args/make/g and we have a nice function for std.conv. -- AndreiOn Monday, 23 May 2016 at 19:00:40 UTC, Adam D. Ruppe wrote:This is very nice... way more clean than I imagined possible.Have I gone completely mad?!?!Yes, though what does it have to do with this thread? :D This is by far the most appealing way to implement named arguments that I've seen so far: https://github.com/CyberShadow/ae/blob/master/utils/meta/args.d
Jun 11 2016
On Saturday, 11 June 2016 at 09:07:43 UTC, Andrei Alexandrescu wrote:No, both are nice to have. If one name is needed for both, "args" is indeed a good commonality. "Invoke function f with these args" and "Construct an object of type T with these args". The problem is it's not very intuitive in usage. Perhaps "call" for functions and "make" for objects are better. If we add these to std.experimental.functional and std.experimental.conv and they get a lot of usage we might make a small change to the language. Andreiwould'nt it be possible to take the type as a runtime param and support struct, class and all callables? e.g. (&fun).args!(a=>5, b=>6); auto st = SomeStruct().args!(a=>3); auto sc = new SomeClass().args!(x => 20, y => 50); (&sc.fun).args!(x => 6); or named and positional args (&sc.fun).args!(x => 9)(3, 6);
Jun 11 2016
On Saturday, 11 June 2016 at 11:15:43 UTC, ArturG wrote:On Saturday, 11 June 2016 at 09:07:43 UTC, Andrei Alexandrescu wrote:Taking an address creates a function pointer, which loses the argument names. (Doesn't it?)No, both are nice to have. If one name is needed for both, "args" is indeed a good commonality. "Invoke function f with these args" and "Construct an object of type T with these args". The problem is it's not very intuitive in usage. Perhaps "call" for functions and "make" for objects are better. If we add these to std.experimental.functional and std.experimental.conv and they get a lot of usage we might make a small change to the language. Andreiwould'nt it be possible to take the type as a runtime param and support struct, class and all callables? e.g. (&fun).args!(a=>5, b=>6);
Jun 11 2016
On Saturday, 11 June 2016 at 15:46:59 UTC, Vladimir Panteleev wrote:Taking an address creates a function pointer, which loses the argument names. (Doesn't it?)unfortunatly yes, but it works as a struct or class initializer https://dpaste.dzfl.pl/6aad852aea90
Jun 11 2016
On Friday, 27 May 2016 at 18:10:59 UTC, Vladimir Panteleev wrote:This is by far the most appealing way to implement named arguments that I've seen so far: https://github.com/CyberShadow/ae/blob/master/utils/meta/args.dAnother cool thing this enables: object initializers. T init(T, Args...)() { import std.traits : FunctionTypeOf; static struct DummyType {} static if (is(T == class)) T r = new T; else T r; foreach (arg; Args) { alias fun = arg!DummyType; static if (is(FunctionTypeOf!fun PT == __parameters)) { enum name = __traits(identifier, PT); foreach (m; __traits(allMembers, T)) { static if (name == m) __traits(getMember, r, m) = fun(DummyType.init); } } } return r; } struct Point { int x, y; } void main() { auto pt = init!(Point, x => 123, y => 456); assert(pt.x == 123 && pt.y == 456); }
Jun 08 2016
On Wednesday, 8 June 2016 at 09:19:46 UTC, John wrote:On Friday, 27 May 2016 at 18:10:59 UTC, Vladimir Panteleev wrote:Already in the link you quoted, FYI.This is by far the most appealing way to implement named arguments that I've seen so far: https://github.com/CyberShadow/ae/blob/master/utils/meta/args.dAnother cool thing this enables: object initializers.
Jun 10 2016
On Monday, 23 May 2016 at 19:00:40 UTC, Adam D. Ruppe wrote:Have I gone completely mad?!?! --- void main() { import std.stdio; writeln(obj!( foo => "bar", baz => 12 )); } --- Prints out: { foo: bar baz: 12 }Pretty snazzy :) Like enums, except not...
May 31 2016