www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - zip with fieldTuple

reply "John" <dont email.me> writes:
So let's say I'm trying to create a really simple ORM. I have a 
struct:

struct foo {
	int a;
	float b;
}

I can iterate over the struct elements with the traits 
FieldTypeTuple!foo, I can iterate over the the string that 
represents the elements I want to shove in the struct, but when I 
try to loop over *both* of these at the same time with zip(...) I 
get an error. Code:

void main() {
	string test = "1,2.0";
     foreach (t, value; zip(FieldTypeTuple!foo, test.split(","))) {
     	writeln(to!t(value));
     }
}

Error:

src\orm.d(13): Error: template std.range.zip does not match any 
function template declaration
C:\D\dmd2\windows\bin\..\..\src\phobos\std\range.d(3808): Error: 
template std.range.zip cannot deduce template function from 
argument types !()((int, float),string[])

I get what the error message is saying, but I have no idea how to 
fix the code to do what I want. I tried to look up what the 
FieldTypeTuple actually returns but it's calling a method on the 
generic type T called tupleOf(), which I can't seem to find (in 
that file or as a general function on object). I'm not sure if 
it's actually a range? I assumed it would be a range of some 
kind, and each of the elements would have a supertype of 
something like 'type' since that's what they are. It could infer 
that now you have two ranges, one of 'type' and one of 'string'.

If I'm able to foreach over two things, shouldn't I be able to 
foreach over the paired ranges with zip? It seems so simple...
Jun 06 2014
next sibling parent "Brad Anderson" <eco gnuk.net> writes:
On Friday, 6 June 2014 at 22:16:36 UTC, John wrote:
 So let's say I'm trying to create a really simple ORM. I have a 
 struct:

 struct foo {
 	int a;
 	float b;
 }

 I can iterate over the struct elements with the traits 
 FieldTypeTuple!foo, I can iterate over the the string that 
 represents the elements I want to shove in the struct, but when 
 I try to loop over *both* of these at the same time with 
 zip(...) I get an error. Code:

 void main() {
 	string test = "1,2.0";
     foreach (t, value; zip(FieldTypeTuple!foo, 
 test.split(","))) {
     	writeln(to!t(value));
     }
 }

 Error:

 src\orm.d(13): Error: template std.range.zip does not match any 
 function template declaration
 C:\D\dmd2\windows\bin\..\..\src\phobos\std\range.d(3808): 
 Error: template std.range.zip cannot deduce template function 
 from argument types !()((int, float),string[])

 I get what the error message is saying, but I have no idea how 
 to fix the code to do what I want. I tried to look up what the 
 FieldTypeTuple actually returns but it's calling a method on 
 the generic type T called tupleOf(), which I can't seem to find 
 (in that file or as a general function on object). I'm not sure 
 if it's actually a range? I assumed it would be a range of some 
 kind, and each of the elements would have a supertype of 
 something like 'type' since that's what they are. It could 
 infer that now you have two ranges, one of 'type' and one of 
 'string'.

 If I'm able to foreach over two things, shouldn't I be able to 
 foreach over the paired ranges with zip? It seems so simple...
foreach-ing over a typetuple is very different from doing it over regular variables. The compiler basically expands the foreach into several blocks of code (without introducing scope, I believe). So you are mixing compile time values and runtime values in a weird way. Types can't be zipped up with runtime values (or zipped up at all without some extra work). One way to do what you want is to foreach over the typetuple and use the index to index into the runtime values like this: struct foo { int a; float b; } void main() { import std.range, std.traits, std.stdio, std.conv; string test = "1,2.0"; auto test_split = test.split(","); foreach (i, T; FieldTypeTuple!foo) { writeln(to!T(test_split[i])); } }
Jun 06 2014
prev sibling next sibling parent reply "bearophile" <bearophileHUGS lycos.com> writes:
John:

 I can iterate over the struct elements with the traits 
 FieldTypeTuple!foo,
In such iteration you are using a static foreach. Types are compile-time constructs in D. If you need run-time entities you need to get their typeinfo.
 I can iterate over the the string that represents the elements 
 I want to shove in the struct,
This is a "dynamic" (regular) foreach.
 but when I try to loop over *both* of these at the same time
 with zip(...) I get an error.
zip only works on run time values. So you can't zip a built-in typetuple of types with a range of values.
 I'm not sure if it's actually a range? I assumed it would be a 
 range of some kind,
It's not a range. FieldTypeTuple returns a built-in typetuple that in this case is really a built-in of types, that are purely compile-time entities.
 and each of the elements would have a supertype of something 
 like 'type' since that's what they are.
They are types (and they aren't other things like uninstantiated templates that in D are another kind), but not even in our dreams there is a supertype for them :-)
 It could infer that now you have two ranges, one of 'type' and 
 one of 'string'.
Nope.
 If I'm able to foreach over two things, shouldn't I be able to 
 foreach over the paired ranges with zip? It seems so simple...
If you turn the built-in typetuple of types into an array or lazy range of typeinfo, then you can zip them. But I don't think this is a good idea. It's better to forget the zipping and use a static foreach on the types, using also an index, and use such index to access the second array of run time values. Bye, bearophile
Jun 06 2014
parent reply "John" <dont email.me> writes:
On Friday, 6 June 2014 at 22:27:38 UTC, bearophile wrote:
 John:

 I can iterate over the struct elements with the traits 
 FieldTypeTuple!foo,
In such iteration you are using a static foreach. Types are compile-time constructs in D. If you need run-time entities you need to get their typeinfo.
I don't want to do that lookup at runtime though. Clearly my intent is to rewrite this foreach zip expression as: auto s = test.split(","); writeln(to!int(s[0])); writeln(to!float(s[1])); or maybe: auto s = test.split(","); writeln(to!int(s.front)); s.popFront(); writeln(to!float(s.front)); But I don't want to actually hand write that each time.
 I can iterate over the the string that represents the elements 
 I want to shove in the struct,
This is a "dynamic" (regular) foreach.
Yes.
 but when I try to loop over *both* of these at the same time
 with zip(...) I get an error.
zip only works on run time values. So you can't zip a built-in typetuple of types with a range of values.
Conceptually that is what I want to do though. I want to pair the type with the string that I'm going to convert.
 I'm not sure if it's actually a range? I assumed it would be a 
 range of some kind,
It's not a range. FieldTypeTuple returns a built-in typetuple that in this case is really a built-in of types, that are purely compile-time entities.
I get that.
 and each of the elements would have a supertype of something 
 like 'type' since that's what they are.
They are types (and they aren't other things like uninstantiated templates that in D are another kind), but not even in our dreams there is a supertype for them :-)
 It could infer that now you have two ranges, one of 'type' and 
 one of 'string'.
Nope.
 If I'm able to foreach over two things, shouldn't I be able to 
 foreach over the paired ranges with zip? It seems so simple...
If you turn the built-in typetuple of types into an array or lazy range of typeinfo, then you can zip them. But I don't think this is a good idea. It's better to forget the zipping and use a static foreach on the types, using also an index, and use such index to access the second array of run time values.
I had already considered a workaround like that, but it's just that. A workaround. You already do unrolling for templates, this isn't much different (at least conceptually). You could basically do exactly what you're describing in the library, no? Have zip loop over all the static/compile time fields (assuming you can separate them in the template from the runtime ranges), and index into (or pop range) the runtime ranges with length checks. The compiler would unroll the compile time ranges (or whatever you want to call them) creating essentially exactly what you've described, the other poster mentioned, and precisely what I put above. Is that not possible?
 Bye,
 bearophile
Jun 06 2014
parent "Brad Anderson" <eco gnuk.net> writes:
On Friday, 6 June 2014 at 23:18:49 UTC, John wrote:
 On Friday, 6 June 2014 at 22:27:38 UTC, bearophile wrote:
 John:

 I can iterate over the struct elements with the traits 
 FieldTypeTuple!foo,
In such iteration you are using a static foreach. Types are compile-time constructs in D. If you need run-time entities you need to get their typeinfo.
I don't want to do that lookup at runtime though. Clearly my intent is to rewrite this foreach zip expression as: auto s = test.split(","); writeln(to!int(s[0])); writeln(to!float(s[1]));
What I wrote in my reply expands to exactly that. It's an array indexing which is very cheap (an addition on a ptr, basically). I can't think of a way to make this any faster if test is a runtime value. If test were a compile time value you could write it in such a way that it distills down to: writeln(1); writeln(2.0); But I suspect that's not what you are going for because test is meant to come in at runtime.
 zip only works on run time values. So you can't zip a built-in 
 typetuple of types with a range of values.
Conceptually that is what I want to do though. I want to pair the type with the string that I'm going to convert.
 I'm not sure if it's actually a range? I assumed it would be 
 a range of some kind,
It's not a range. FieldTypeTuple returns a built-in typetuple that in this case is really a built-in of types, that are purely compile-time entities.
I get that.
 and each of the elements would have a supertype of something 
 like 'type' since that's what they are.
They are types (and they aren't other things like uninstantiated templates that in D are another kind), but not even in our dreams there is a supertype for them :-)
 It could infer that now you have two ranges, one of 'type' 
 and one of 'string'.
Nope.
 If I'm able to foreach over two things, shouldn't I be able 
 to foreach over the paired ranges with zip? It seems so 
 simple...
If you turn the built-in typetuple of types into an array or lazy range of typeinfo, then you can zip them. But I don't think this is a good idea. It's better to forget the zipping and use a static foreach on the types, using also an index, and use such index to access the second array of run time values.
I had already considered a workaround like that, but it's just that. A workaround. You already do unrolling for templates, this isn't much different (at least conceptually). You could basically do exactly what you're describing in the library, no? Have zip loop over all the static/compile time fields (assuming you can separate them in the template from the runtime ranges), and index into (or pop range) the runtime ranges with length checks. The compiler would unroll the compile time ranges (or whatever you want to call them) creating essentially exactly what you've described, the other poster mentioned, and precisely what I put above. Is that not possible?
You could do something kind of like what you are describing, yes, but you have to remember that compile time values are passed in as template parameters. It'd have to look something like: zipWithTypes!(FieldTypeTuple!foo)(test.split(",")) And the element type of the result would have to be something like Variant if it's going to be a range. You could use a Tuple too if you don't care about the result having a range interface. Or you could represent types in runtime code with enums or something, like bearophile was saying.
Jun 06 2014
prev sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
John:

 C:\D\dmd2\windows\bin\..\..\src\phobos\std\range.d(3808): 
 Error: template std.range.zip cannot deduce template function 
 from argument types !()((int, float),string[])
By the way, I find it surprising that D/Phobos give a so good error message for a so messed up situation. I don't think there are many system languages able to give a so nice error message when you try to zip types with values. Bye, bearophile
Jun 06 2014