www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Why tuples are not ranges?

reply Mr.Bingo <Bingo Namo.com> writes:
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
next sibling parent Timoses <timosesu gmail.com> writes:
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
prev sibling parent reply Alex <sascha.orlov gmail.com> writes:
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
parent reply Mr.Bingo <Bingo Namo.com> writes:
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:
 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()); } ´´´
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.
Jun 28 2018
next sibling parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
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
parent reply Mr.Bingo <Bingo Namo.com> writes:
On Thursday, 28 June 2018 at 18:03:09 UTC, Ali Çehreli wrote:
 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
Thanks, why not add the ability to pass through ranges and arrays and add it to phobos?
Jun 28 2018
parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
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
parent reply Alex <sascha.orlov gmail.com> writes:
On Thursday, 28 June 2018 at 19:02:51 UTC, Ali Çehreli wrote:
 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
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?
Jun 28 2018
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
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:
 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
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?
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 Davis
Jun 28 2018
parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
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.
 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 to 
have
 the same type - and that would be pretty weird.
Agreed. Ali
Jun 29 2018
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
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
parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 06/30/2018 05:17 AM, Steven Schveighoffer wrote:

 Isn't this what only does? https://dlang.org/phobos/std_range.html#only
Cool. :) import std.range : only; auto t = tuple(5, 3.5, false); auto r = only(t.expand); Ali
Jun 30 2018
prev sibling parent Meta <jared771 gmail.com> writes:
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