www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Tuple enhancement

reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
I was thinking it would be handy if tuples had a way to access a field 
by name at runtime. E.g.:

Tuple!(int, "a", double, "b") t;
string x = condition ? "a" : "b";
double v = t.get!string(x, 3.14);

The get method takes the field name and the type of the presumed field, 
and it returns the value of the field in the tuple. If no field by that 
name and type, return the second argument.

Rquirements:

* Do not throw - allow the second argument to be a throwing delegate

* Do not add overhead if the method is never used

* Figure out a reasonable (but not all too complicated) way to deal with 
implicit conversions, e.g. if x == "a" in the the code above, should it 
return 3.14 or convert the int to double?

* Handle qualifiers appropriately


Andrei
Oct 16 2016
next sibling parent reply Dmitry Olshansky <dmitry.olsh gmail.com> writes:
On 10/16/16 3:58 PM, Andrei Alexandrescu wrote:
 I was thinking it would be handy if tuples had a way to access a field
 by name at runtime. E.g.:

 Tuple!(int, "a", double, "b") t;
 string x = condition ? "a" : "b";
 double v = t.get!string(x, 3.14);
I swear I've seen a pull request merged that adds an accessible "names" AliasSeq with names of fields. That should be a good starting point.
 The get method takes the field name and the type of the presumed field,
 and it returns the value of the field in the tuple. If no field by that
 name and type, return the second argument.

 Rquirements:

 * Do not throw - allow the second argument to be a throwing delegate

 * Do not add overhead if the method is never used

 * Figure out a reasonable (but not all too complicated) way to deal with
 implicit conversions, e.g. if x == "a" in the the code above, should it
 return 3.14 or convert the int to double?

 * Handle qualifiers appropriately


 Andrei
Oct 16 2016
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 10/16/2016 11:07 AM, Dmitry Olshansky wrote:
 On 10/16/16 3:58 PM, Andrei Alexandrescu wrote:
 I was thinking it would be handy if tuples had a way to access a field
 by name at runtime. E.g.:

 Tuple!(int, "a", double, "b") t;
 string x = condition ? "a" : "b";
 double v = t.get!string(x, 3.14);
I swear I've seen a pull request merged that adds an accessible "names" AliasSeq with names of fields. That should be a good starting point.
Sadly it was closed, not merged. And it was a bit more general allowing getting a field by name from any struct. -- Andrei
Oct 16 2016
parent Jack Stouffer <jack jackstouffer.com> writes:
On Sunday, 16 October 2016 at 16:49:43 UTC, Andrei Alexandrescu 
wrote:
 Sadly it was closed, not merged. And it was a bit more general 
 allowing getting a field by name from any struct. -- Andrei
The PR in question if anyone is interested: https://github.com/dlang/phobos/pull/4154 I currently lack the time to resurrect this, so anyone is free to take my code if they wish.
Oct 17 2016
prev sibling next sibling parent reply Sebastiaan Koppe <mail skoppe.eu> writes:
On Sunday, 16 October 2016 at 13:58:51 UTC, Andrei Alexandrescu 
wrote:
 I was thinking it would be handy if tuples had a way to access 
 a field by name at runtime. E.g.:

 Tuple!(int, "a", double, "b") t;
 string x = condition ? "a" : "b";
 double v = t.get!string(x, 3.14);
That would mean that tuple then needs to keep the strings around - taking up space. That plus the fact that the following code is equivalent (obviously): Tuple!(int, "a", double, "b") t; double v = condition ? t.a : t.b; Why this addition? What does it enable?
Oct 16 2016
next sibling parent reply Chris Wright <dhasenan gmail.com> writes:
On Sun, 16 Oct 2016 18:51:06 +0000, Sebastiaan Koppe wrote:

 On Sunday, 16 October 2016 at 13:58:51 UTC, Andrei Alexandrescu wrote:
 I was thinking it would be handy if tuples had a way to access a field
 by name at runtime. E.g.:

 Tuple!(int, "a", double, "b") t;
 string x = condition ? "a" : "b";
 double v = t.get!string(x, 3.14);
That would mean that tuple then needs to keep the strings around - taking up space.
It doesn't change Tuple.sizeof.
 That plus the fact that the following code is
 equivalent (obviously):
 
 Tuple!(int, "a", double, "b") t;
 double v = condition ? t.a : t.b;
 
 Why this addition? What does it enable?
It's a little bit of runtime reflection, which people sometimes find handy. There's nothing stopping you from writing it manually or with metaprogramming, granted. And if it's just tuples, it doesn't seem like much of an advantage.
Oct 16 2016
next sibling parent Temtaime <temtaime gmail.com> writes:
On Sunday, 16 October 2016 at 20:11:01 UTC, Chris Wright wrote:
 On Sun, 16 Oct 2016 18:51:06 +0000, Sebastiaan Koppe wrote:

 On Sunday, 16 October 2016 at 13:58:51 UTC, Andrei 
 Alexandrescu wrote:
 I was thinking it would be handy if tuples had a way to 
 access a field by name at runtime. E.g.:

 Tuple!(int, "a", double, "b") t;
 string x = condition ? "a" : "b";
 double v = t.get!string(x, 3.14);
That would mean that tuple then needs to keep the strings around - taking up space.
It doesn't change Tuple.sizeof.
 That plus the fact that the following code is
 equivalent (obviously):
 
 Tuple!(int, "a", double, "b") t;
 double v = condition ? t.a : t.b;
 
 Why this addition? What does it enable?
It's a little bit of runtime reflection, which people sometimes find handy. There's nothing stopping you from writing it manually or with metaprogramming, granted. And if it's just tuples, it doesn't seem like much of an advantage.
Please, not. We already have great compile time reflection.
Oct 16 2016
prev sibling parent Sebastiaan Koppe <mail skoppe.eu> writes:
On Sunday, 16 October 2016 at 20:11:01 UTC, Chris Wright wrote:
 On Sun, 16 Oct 2016 18:51:06 +0000, Sebastiaan Koppe wrote:
 That would mean that tuple then needs to keep the strings 
 around - taking up space.
It doesn't change Tuple.sizeof.
Sure, but it still takes up space in the executable. Negligible of course, but for something that doesn't bring anything to the table, not so.
 It's a little bit of runtime reflection, which people sometimes 
 find handy. There's nothing stopping you from writing it 
 manually or with metaprogramming, granted. And if it's just 
 tuples, it doesn't seem like much of an advantage.
Exactly. When you want runtime reflection, would you put it in a tuple?
Oct 17 2016
prev sibling parent Kapps <opantm2+spam gmail.com> writes:
On Sunday, 16 October 2016 at 18:51:06 UTC, Sebastiaan Koppe 
wrote:
 On Sunday, 16 October 2016 at 13:58:51 UTC, Andrei Alexandrescu 
 wrote:
 I was thinking it would be handy if tuples had a way to access 
 a field by name at runtime. E.g.:

 Tuple!(int, "a", double, "b") t;
 string x = condition ? "a" : "b";
 double v = t.get!string(x, 3.14);
That would mean that tuple then needs to keep the strings around - taking up space. That plus the fact that the following code is equivalent (obviously): Tuple!(int, "a", double, "b") t; double v = condition ? t.a : t.b; Why this addition? What does it enable?
I don't think this would actually require storing any additional data. It'd be more like: struct Tuple(Aliases...) { auto get(T)(string name) { foreach(alias, i; ExtractNames!Aliases) { if(alias == name) return this[i]; } } } Which is the advantage over runtime reflection in this case. It does seem a bit arbitrary to have Tuple include it though. But given that it's a template method (therefore won't be compiled in if not accessed for this particular Tuple type) and requires storing no data in the instance, there's zero overhead if you're not actually using the feature.
Oct 16 2016
prev sibling next sibling parent sarn <sarn theartofmachinery.com> writes:
On Sunday, 16 October 2016 at 13:58:51 UTC, Andrei Alexandrescu 
wrote:
 I was thinking it would be handy if tuples had a way to access 
 a field by name at runtime. E.g.:

 Tuple!(int, "a", double, "b") t;
 string x = condition ? "a" : "b";
 double v = t.get!string(x, 3.14);

 The get method takes the field name and the type of the 
 presumed field, and it returns the value of the field in the 
 tuple. If no field by that name and type, return the second 
 argument.

 Rquirements:

 * Do not throw - allow the second argument to be a throwing 
 delegate

 * Do not add overhead if the method is never used

 * Figure out a reasonable (but not all too complicated) way to 
 deal with implicit conversions, e.g. if x == "a" in the the 
 code above, should it return 3.14 or convert the int to double?

 * Handle qualifiers appropriately


 Andrei
Personally, if I wanted this, I'd be using a hash table with Variant objects in it or something. An argument for keeping tuple features simple is that they really aren't a good tool for doing anything fancy. If you need a runtime interface, you're probably outgrowing tuples already. Why not make a struct that contains a hash table?
Oct 16 2016
prev sibling next sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 10/16/16 9:58 AM, Andrei Alexandrescu wrote:
 I was thinking it would be handy if tuples had a way to access a field
 by name at runtime. E.g.:

 Tuple!(int, "a", double, "b") t;
 string x = condition ? "a" : "b";
 double v = t.get!string(x, 3.14);

 The get method takes the field name and the type of the presumed field,
 and it returns the value of the field in the tuple. If no field by that
 name and type, return the second argument.

 Rquirements:

 * Do not throw - allow the second argument to be a throwing delegate

 * Do not add overhead if the method is never used

 * Figure out a reasonable (but not all too complicated) way to deal with
 implicit conversions, e.g. if x == "a" in the the code above, should it
 return 3.14 or convert the int to double?

 * Handle qualifiers appropriately
Why not something like this: Val get(T, Val)(auto ref T item, string memberName, Val defaultValue) { switch(memberName) { foreach(n; __traits(allMembers, T)) { static if(is(typeof(__traits(getMember, item, n)) : Val)) case n: mixin("return item." ~ n ~ ";"); } default: return defaultValue; } } 1. It's opt-in, no code is added to the binary if you don't use it. 2. works for any type, not just Tuple. Note: no testing, this needs probably some work to get it reasonable, e.g. need to filter out private members. Maybe Jack Stouffer's PR is a better start. -Steve
Oct 17 2016
parent reply Edwin van Leeuwen <edder tkwsping.nl> writes:
On Monday, 17 October 2016 at 13:35:12 UTC, Steven Schveighoffer 
wrote:
 Why not something like this:

 Val get(T, Val)(auto ref T item, string memberName, Val 
 defaultValue)
 {
    switch(memberName)
    {
    foreach(n; __traits(allMembers, T))
    {
       static if(is(typeof(__traits(getMember, item, n)) : Val))
         case n: mixin("return item." ~ n ~ ";");
    }
    default:
      return defaultValue;
    }
 }
I like the general solution, although I probably would just expect it to fail compiling if no member is present or return Val.init if no defaultValue is passed.
Oct 17 2016
parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 10/17/16 9:42 AM, Edwin van Leeuwen wrote:
 On Monday, 17 October 2016 at 13:35:12 UTC, Steven Schveighoffer wrote:
 Why not something like this:

 Val get(T, Val)(auto ref T item, string memberName, Val defaultValue)
 {
    switch(memberName)
    {
    foreach(n; __traits(allMembers, T))
    {
       static if(is(typeof(__traits(getMember, item, n)) : Val))
         case n: mixin("return item." ~ n ~ ";");
    }
    default:
      return defaultValue;
    }
 }
I like the general solution, although I probably would just expect it to fail compiling if no member is present or return Val.init if no defaultValue is passed.
Hm... this could work: Val get(Val, T)(auto ref T item, string memberName, Val defaultValue = Val.init) int x = t.get("a", 1); int x = t.get!int("a"); -Steve
Oct 17 2016
prev sibling next sibling parent Bienlein <jeti789 web.de> writes:
On Sunday, 16 October 2016 at 13:58:51 UTC, Andrei Alexandrescu 
wrote:
 I was thinking it would be handy if tuples had a way to access 
 a field by name at runtime. E.g.:

 Tuple!(int, "a", double, "b") t;
 string x = condition ? "a" : "b";
In Scala you can do this: def getUserInfo = ("Al", 42, 200.0) val(name, age, weight) = getUserInfo See http://alvinalexander.com/scala/scala-tuple-examples-syntax. You can also do the same thing in Kotlin and in Groovy (in Groovy it only works if static type checking is not turned on, though). I fear if D in contrast has a solution for tuples like this: Tuple!(int, "a", double, "b") t; string x = condition ? "a" : "b"; double v = t.get!string(x, 3.14); where variable names are mapped as strings it could harm the reputation of the language. People have something they could easily use as a sample to attack the language. These are harsh words, I know. Sorry for that. I only write this because I feel people should be advised not to do this. Real tuples or none. IMHO much better.
Oct 18 2016
prev sibling parent Idan Arye <GenericNPC gmail.com> writes:
On Sunday, 16 October 2016 at 13:58:51 UTC, Andrei Alexandrescu 
wrote:
 I was thinking it would be handy if tuples had a way to access 
 a field by name at runtime. E.g.:

 Tuple!(int, "a", double, "b") t;
 string x = condition ? "a" : "b";
 double v = t.get!string(x, 3.14);

 The get method takes the field name and the type of the 
 presumed field, and it returns the value of the field in the 
 tuple. If no field by that name and type, return the second 
 argument.

 Rquirements:

 * Do not throw - allow the second argument to be a throwing 
 delegate

 * Do not add overhead if the method is never used

 * Figure out a reasonable (but not all too complicated) way to 
 deal with implicit conversions, e.g. if x == "a" in the the 
 code above, should it return 3.14 or convert the int to double?

 * Handle qualifiers appropriately


 Andrei
When I need to choose at runtime a value out of multiple choices with different types(all available at compile-time), and handle them similarly, I like to use something like this: import std.stdio; import std.typecons; import std.conv; auto getAnd(alias dlg, T)(T tup, string key) { final switch (key) foreach (fieldName; T.fieldNames) { case fieldName: return dlg(__traits(getMember, tup, fieldName)); } } void main() { Tuple!(int, "a", double, "b") t; t.a = 3; t.b = 3.14; string toString(string key) { return t.getAnd!(x => x.to!string)(key); } assert (toString("a") == "3"); assert (toString("b") == "3.14"); } The idea is to pass a delegate as an "alias", and instantiate it multiple times, once for each field. This means that instead of being forced to convert them to a common type, we can write once code that uses the correct type for each field.
Oct 18 2016