Jump to page: 1 2 3
Thread overview
foo => "bar" key/value literals in D!
May 23, 2016
Adam D. Ruppe
May 23, 2016
Daniel N
May 24, 2016
Adam D. Ruppe
May 25, 2016
Daniel N
May 25, 2016
Jacob Carlborg
May 25, 2016
Daniel N
May 25, 2016
Jacob Carlborg
May 25, 2016
Meta
May 26, 2016
Jacob Carlborg
May 25, 2016
Adam D. Ruppe
May 24, 2016
Meta
May 24, 2016
Meta
May 24, 2016
Adam D. Ruppe
May 24, 2016
Bauss
May 24, 2016
Jacob Carlborg
May 25, 2016
Adam D. Ruppe
May 27, 2016
Vladimir Panteleev
May 28, 2016
Taylor Hillegeist
Jun 11, 2016
Vladimir Panteleev
Jun 11, 2016
ArturG
Jun 11, 2016
Vladimir Panteleev
Jun 11, 2016
ArturG
Jun 08, 2016
John
Jun 11, 2016
Vladimir Panteleev
May 31, 2016
Era Scarecrow
May 23, 2016
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 24, 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 24, 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 24, 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 24, 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 24, 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!

It's like a better version of "dynamic" from C#.
May 24, 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 24, 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 25, 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:
>> 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

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.

« First   ‹ Prev
1 2 3