digitalmars.D.learn - Why tuples are not ranges?
- Mr.Bingo (12/12) Jun 28 2018 Seems like it would unify things quite a bit.
- Timoses (13/25) Jun 28 2018 You could make a range out of tuples. This seems to work:
- Alex (16/17) Jun 28 2018 Yeah... this is, because you can't popFront on a tuple, as the
- Mr.Bingo (22/39) Jun 28 2018 But is this going to be optimized?
- =?UTF-8?Q?Ali_=c3=87ehreli?= (48/54) Jun 28 2018 Similar to the array-slice distinction, tuple is a container, needing
- Mr.Bingo (3/61) Jun 28 2018 Thanks, why not add the ability to pass through ranges and arrays
- =?UTF-8?Q?Ali_=c3=87ehreli?= (4/6) Jun 28 2018 Makes sense. It needs an enhancement request at
- Alex (12/20) Jun 28 2018 Wouldn't this be weird from the semantic view?
- Jonathan M Davis (18/42) Jun 28 2018 It wouldn't make any sense to turn a Tuple into a range. However, if all...
- =?UTF-8?Q?Ali_=c3=87ehreli?= (12/23) Jun 29 2018 I agree with all your concerns. The fact that Meta decided to make the
- Steven Schveighoffer (3/19) Jun 30 2018 Isn't this what only does? https://dlang.org/phobos/std_range.html#only
- =?UTF-8?Q?Ali_=c3=87ehreli?= (6/7) Jun 30 2018 Cool. :)
- Meta (22/28) Jun 28 2018 You're right; it can definitely be done.
Seems like it would unify things quite a bit. import std.typecons, std.range, std.array, std.algorithm, std.stdio; void main() { auto t = tuple(3,4,5,6); //auto t = [3,4,5,6]; writeln(t.map!(a => 3*a).sum()); }
Jun 28 2018
On Thursday, 28 June 2018 at 14:35:33 UTC, Mr.Bingo wrote:Seems like it would unify things quite a bit. import std.typecons, std.range, std.array, std.algorithm, std.stdio; void main() { auto t = tuple(3,4,5,6); //auto t = [3,4,5,6]; writeln(t.map!(a => 3*a).sum()); }You could make a range out of tuples. This seems to work: import std.typecons, std.range, std.array, std.algorithm, std.stdio; void main() { auto t = tuple(3,4,5,6); auto m = t.expand.only; writeln(m.map!(a => 3*a).sum()); } Tuple.expand will expand the elements when calling a function (in this case only from std.range). https://dlang.org/library/std/typecons/tuple.expand.html
Jun 28 2018
On Thursday, 28 June 2018 at 14:35:33 UTC, Mr.Bingo wrote:Seems like it would unify things quite a bit.Yeah... this is, because you can't popFront on a tuple, as the amount of entries is fixed. You can, however, popFront on every range. But as Timoses wrote you can easily make a range out of a tuple, for example by slicing: ´´´ import std.typecons, std.range, std.array, std.algorithm, std.stdio; void main() { auto t = tuple(3,4,5,6); auto m = [t[]]; writeln(m.map!(a => 3*a).sum()); } ´´´
Jun 28 2018
On Thursday, 28 June 2018 at 16:02:59 UTC, Alex wrote:On Thursday, 28 June 2018 at 14:35:33 UTC, Mr.Bingo wrote:But is this going to be optimized? BTW, surely the tuple has a popFront! Just pop the last element and returns a new tuple. That is, a tuple is a range! Just because it doesn't implement the proper functions doesn't mean it can't do so. It is clearly easy to see if a tuple is empty, to get the front, and to pop the front and return a new tuple with n - 1 elements, which is really just the tuple(a sliced tuple, say) with the first member hidden. Since a tuple is fixed at compile time you can "virtually" pop the elements, it doesn't mean that a tuple is not a range. So, maybe for it to work the compiler needs to be able to slice tuples efficiently(not convert to dynamic arrays). This is moot if the compiler can realize that it can do most of the work at compile time but I'm not so sure it can. I mean, if you think about it, the memory layout of a tuple is sequential types: T1 T2 ... So, to popFront a tuple is just changing the starting offset.Seems like it would unify things quite a bit.Yeah... this is, because you can't popFront on a tuple, as the amount of entries is fixed. You can, however, popFront on every range. But as Timoses wrote you can easily make a range out of a tuple, for example by slicing: ´´´ import std.typecons, std.range, std.array, std.algorithm, std.stdio; void main() { auto t = tuple(3,4,5,6); auto m = [t[]]; writeln(m.map!(a => 3*a).sum()); } ´´´
Jun 28 2018
On 06/28/2018 10:00 AM, Mr.Bingo wrote:But is this going to be optimized?Not our job! :o)That is, a tuple is a range!Similar to the array-slice distinction, tuple is a container, needing its range.It is clearly easy to see if a tuple is empty, to get the front,Ok.and to pop the front and return a new tuple with n - 1 elements, which is really just the tuple(a sliced tuple, say) with the first member hidden.That would be special for tuples because popFront does not return a new range (and definitely not with a new type) but mutates the existing one. Here is a quick and dirty library solution: // TODO: Template constraints auto rangified(T)(T t) { import std.traits : CommonType; import std.conv : to; alias ElementType = CommonType!(T.tupleof); struct Range { size_t i; bool empty() { return i >= t.length; } ElementType front() { final switch (i) { static foreach (j; 0 .. t.length) { case j: return t[j].to!ElementType; } } } void popFront() { ++i; } enum length = t.length; // TODO: save(), opIndex(), etc. } return Range(); } unittest { import std.typecons : tuple; import std.stdio : writefln; import std.range : ElementType; auto t = tuple(5, 3.5, false); auto r = t.rangified; writefln("%s elements of '%s': %(%s, %)", r.length, ElementType!(typeof(r)).stringof, r); } void main() { } Prints 3 elements of 'double': 5, 3.5, 0 Ali
Jun 28 2018
On Thursday, 28 June 2018 at 18:03:09 UTC, Ali Çehreli wrote:On 06/28/2018 10:00 AM, Mr.Bingo wrote:Thanks, why not add the ability to pass through ranges and arrays and add it to phobos?But is this going to be optimized?Not our job! :o)That is, a tuple is a range!Similar to the array-slice distinction, tuple is a container, needing its range.It is clearly easy to see if a tuple is empty, to get thefront, Ok.and to pop the front and return a new tuple with n - 1 elements,which isreally just the tuple(a sliced tuple, say) with the firstmember hidden. That would be special for tuples because popFront does not return a new range (and definitely not with a new type) but mutates the existing one. Here is a quick and dirty library solution: // TODO: Template constraints auto rangified(T)(T t) { import std.traits : CommonType; import std.conv : to; alias ElementType = CommonType!(T.tupleof); struct Range { size_t i; bool empty() { return i >= t.length; } ElementType front() { final switch (i) { static foreach (j; 0 .. t.length) { case j: return t[j].to!ElementType; } } } void popFront() { ++i; } enum length = t.length; // TODO: save(), opIndex(), etc. } return Range(); } unittest { import std.typecons : tuple; import std.stdio : writefln; import std.range : ElementType; auto t = tuple(5, 3.5, false); auto r = t.rangified; writefln("%s elements of '%s': %(%s, %)", r.length, ElementType!(typeof(r)).stringof, r); } void main() { } Prints 3 elements of 'double': 5, 3.5, 0 Ali
Jun 28 2018
On 06/28/2018 11:08 AM, Mr.Bingo wrote:Thanks, why not add the ability to pass through ranges and arrays and add it to phobos?Makes sense. It needs an enhancement request at http://issues.dlang.org/, a good implementation, and a pull request. :) Ali
Jun 28 2018
On Thursday, 28 June 2018 at 19:02:51 UTC, Ali Çehreli wrote:On 06/28/2018 11:08 AM, Mr.Bingo wrote:Wouldn't this be weird from the semantic view? Assume, I define a tuple with named fields "key" and "value", where a popFront is defined. Now, I use popFront. Is the "key" field still available? Why? Or, why not? What happens, if I name a field of a tuple "front"? Is it available after using popFront? And where does it point to? I mean, I use something similar in my own project, but doesn't one has to make a clear distinction between a container and a slice of this container? Or should static arrays be unified in the same manner?Thanks, why not add the ability to pass through ranges andarrays andadd it to phobos?Makes sense. It needs an enhancement request at http://issues.dlang.org/, a good implementation, and a pull request. :) Ali
Jun 28 2018
On Friday, June 29, 2018 05:52:03 Alex via Digitalmars-d-learn wrote:On Thursday, 28 June 2018 at 19:02:51 UTC, Ali Çehreli wrote:It wouldn't make any sense to turn a Tuple into a range. However, if all of the values are of the same type, it might make sense to create a range from each of the values in the Tuple. Just like static arrays, they're indexable, so there's a clear order to them. So, I suppose that Tuple could be made sliceable, or a helper function could be created that takes a Tuple and returns a range which wraps it. However, Tuples frequently have different types for each of their members (in which case, creating a range from a Tuple makes no sense at all), and really, a Tuple is essentially a way to declare a POD struct of values without explicitly declaring a struct. And at that point, talking about getting a range over a Tuple is basically the same thing as talking about creating a range from an arbitrary struct whose members all happen to have the same type - and that would be pretty weird. So, under limited circumstances, a range could be constructed from a Tuple, but in general, it just strikes me as a bizarre thing to do. Tuples and ranges are very distinct concepts, and Tuples aren't really containers. - Jonathan M DavisOn 06/28/2018 11:08 AM, Mr.Bingo wrote:Wouldn't this be weird from the semantic view? Assume, I define a tuple with named fields "key" and "value", where a popFront is defined. Now, I use popFront. Is the "key" field still available? Why? Or, why not? What happens, if I name a field of a tuple "front"? Is it available after using popFront? And where does it point to? I mean, I use something similar in my own project, but doesn't one has to make a clear distinction between a container and a slice of this container? Or should static arrays be unified in the same manner?Thanks, why not add the ability to pass through ranges andarrays andadd it to phobos?Makes sense. It needs an enhancement request at http://issues.dlang.org/, a good implementation, and a pull request. :) Ali
Jun 28 2018
On 06/28/2018 11:10 PM, Jonathan M Davis wrote:On Friday, June 29, 2018 05:52:03 Alex via Digitalmars-d-learn wrote:I agree with all your concerns. The fact that Meta decided to make the element type Algebraic!T as opposed to my CommonType!T choice is another semantic problem.Wouldn't this be weird from the semantic view?It wouldn't make any sense to turn a Tuple into a range. However, ifall ofthe values are of the same type, it might make sense to create arange fromeach of the values in the Tuple.I went a step further and used CommonType!T.a helper function could be created that takes a Tuple and returns a range which wraps it.That's what my example did (with added bugs like 'length' being an enum by mistake).And at that point, talking about getting a range over a Tuple is basically the same thing as talking about creating a range from an arbitrary struct whose members all happen tohavethe same type - and that would be pretty weird.Agreed. Ali
Jun 29 2018
On 6/29/18 1:35 PM, Ali Çehreli wrote:On 06/28/2018 11:10 PM, Jonathan M Davis wrote: > On Friday, June 29, 2018 05:52:03 Alex via Digitalmars-d-learn wrote: >> Wouldn't this be weird from the semantic view? I agree with all your concerns. The fact that Meta decided to make the element type Algebraic!T as opposed to my CommonType!T choice is another semantic problem. > It wouldn't make any sense to turn a Tuple into a range. However, if all of > the values are of the same type, it might make sense to create a range from > each of the values in the Tuple. I went a step further and used CommonType!T.Isn't this what only does? https://dlang.org/phobos/std_range.html#only -Steve
Jun 30 2018
On 06/30/2018 05:17 AM, Steven Schveighoffer wrote:Isn't this what only does? https://dlang.org/phobos/std_range.html#onlyCool. :) import std.range : only; auto t = tuple(5, 3.5, false); auto r = only(t.expand); Ali
Jun 30 2018
On Thursday, 28 June 2018 at 17:00:37 UTC, Mr.Bingo wrote:I mean, if you think about it, the memory layout of a tuple is sequential types: T1 T2 ... So, to popFront a tuple is just changing the starting offset.You're right; it can definitely be done. struct TupleRange(T...) { size_t index; Tuple!T store; property length() { assert(index <= store.length); return store.length - index; } Algebraic!T front() { assert(length > 0); return typeof(return)(store[index]); } void popFront() { assert(length > 0); index++; } }
Jun 28 2018