www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Array types not treated uniformly when passed as ranges

reply jam <gr0v3er gmail.com> writes:
Hi all,

Just curious as to the difference in the built-in variable length
array vs. the std.container.Array and fixed length arrays when it
comes to using them in functions that take Ranges.

For instance the following does not compile:

import std.algorithm;
import std.stdio;
import std.range;
import std.conv;
import std.container;
import std.array;

void main() {

    int[5] builtin_fixed;
    int[] builtin_variable;
    Array!(int) con_array;

    con_array.length(5);
    builtin_variable.length = 5;

    fill(builtin_variable, 9); //ok, no error
    isSorted(builtin_variable); //ditto

    //The following 4 statements produce errors
    fill(builtin_fixed, 9);
    fill(con_array, 9);

    isSorted(con_array);
    isSorted(builtin_fixed);

}

The errors are variations on:

Error: template std.algorithm.fill(Range,Value) if
(isForwardRange!(Range) && is(typeof(range.front = filler))) does not
match any function template declaration
Error: template std.algorithm.fill(Range,Value) if
(isForwardRange!(Range) && is(typeof(range.front = filler))) cannot
deduce template function from argument types !()(int[5LU],int)

If I change those 4 statements to:

    fill(builtin_fixed[], 9);
    fill(con_array[], 9);

    isSorted(con_array[]);
    isSorted(builtin_fixed[]);

effectively passing ranges (std.container.Array!(int).Array.Range in
the case of con_array, and int[] for builtin_fixed) which then works
as expected.  This all makes sense, and it's easy enough to write
wrappers,   but I would (well and I did) expect the first way to just
work.   This may just be a nitpick I guess, but being new to the
language this little detour involved quite a bit of time research (not
a bad thing, I did learn quite a bit in the process), but makes me
wonder if I am missing something fundamental regarding when I should
be using these different array types.

Cheers
Feb 14 2011
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 14 Feb 2011 21:18:39 -0500, jam <gr0v3er gmail.com> wrote:

 Hi all,

 Just curious as to the difference in the built-in variable length
 array vs. the std.container.Array and fixed length arrays when it
 comes to using them in functions that take Ranges.

 For instance the following does not compile:

 import std.algorithm;
 import std.stdio;
 import std.range;
 import std.conv;
 import std.container;
 import std.array;

 void main() {

     int[5] builtin_fixed;
     int[] builtin_variable;
     Array!(int) con_array;

     con_array.length(5);
     builtin_variable.length = 5;

     fill(builtin_variable, 9); //ok, no error
     isSorted(builtin_variable); //ditto

     //The following 4 statements produce errors
     fill(builtin_fixed, 9);
     fill(con_array, 9);

     isSorted(con_array);
     isSorted(builtin_fixed);

 }

 The errors are variations on:

 Error: template std.algorithm.fill(Range,Value) if
 (isForwardRange!(Range) && is(typeof(range.front = filler))) does not
 match any function template declaration
 Error: template std.algorithm.fill(Range,Value) if
 (isForwardRange!(Range) && is(typeof(range.front = filler))) cannot
 deduce template function from argument types !()(int[5LU],int)

 If I change those 4 statements to:

     fill(builtin_fixed[], 9);
     fill(con_array[], 9);

     isSorted(con_array[]);
     isSorted(builtin_fixed[]);

 effectively passing ranges (std.container.Array!(int).Array.Range in
 the case of con_array, and int[] for builtin_fixed) which then works
 as expected.  This all makes sense, and it's easy enough to write
 wrappers,   but I would (well and I did) expect the first way to just
 work.   This may just be a nitpick I guess, but being new to the
 language this little detour involved quite a bit of time research (not
 a bad thing, I did learn quite a bit in the process), but makes me
 wonder if I am missing something fundamental regarding when I should
 be using these different array types.
This is because: 1. a fixed-sized array is not a range. It is passed by value, not by reference. The problem there is IFTI thinking you want to pass the fixed array as a fixed array and not as a slice. 2. a container is not a range. An Array is a container, so you must extract a range from it to use it in range-like activities. The second point seems odd, since builtin arrays are ranges, but an Array is different because it's a true reference type (changing the length from one reference alters the length of another reference) and it "owns" the memory contained within, unlike a builtin array which just references the memory. But it's also easy to consider other things. Consider a RedBlackTree container, should that be a range? What happens when you popFront? -Steve
Feb 15 2011
next sibling parent jam <gr0v3er+d gmail.com> writes:
On Tue, 15 Feb 2011 09:00:54 -0500, Steven Schveighoffer wrote:

 On Mon, 14 Feb 2011 21:18:39 -0500, jam <gr0v3er gmail.com> wrote:
 
 Hi all,

 Just curious as to the difference in the built-in variable length array
 vs. the std.container.Array and fixed length arrays when it comes to
 using them in functions that take Ranges.

 For instance the following does not compile:

 import std.algorithm;
 import std.stdio;
 import std.range;
 import std.conv;
 import std.container;
 import std.array;

 void main() {

     int[5] builtin_fixed;
     int[] builtin_variable;
     Array!(int) con_array;

     con_array.length(5);
     builtin_variable.length = 5;

     fill(builtin_variable, 9); //ok, no error
     isSorted(builtin_variable); //ditto

     //The following 4 statements produce errors fill(builtin_fixed, 9);
     fill(con_array, 9);

     isSorted(con_array);
     isSorted(builtin_fixed);

 }

 The errors are variations on:

 Error: template std.algorithm.fill(Range,Value) if
 (isForwardRange!(Range) && is(typeof(range.front = filler))) does not
 match any function template declaration Error: template
 std.algorithm.fill(Range,Value) if (isForwardRange!(Range) &&
 is(typeof(range.front = filler))) cannot deduce template function from
 argument types !()(int[5LU],int)

 If I change those 4 statements to:

     fill(builtin_fixed[], 9);
     fill(con_array[], 9);

     isSorted(con_array[]);
     isSorted(builtin_fixed[]);

 effectively passing ranges (std.container.Array!(int).Array.Range in
 the case of con_array, and int[] for builtin_fixed) which then works as
 expected.  This all makes sense, and it's easy enough to write
 wrappers,   but I would (well and I did) expect the first way to just
 work.   This may just be a nitpick I guess, but being new to the
 language this little detour involved quite a bit of time research (not
 a bad thing, I did learn quite a bit in the process), but makes me
 wonder if I am missing something fundamental regarding when I should be
 using these different array types.
This is because: 1. a fixed-sized array is not a range. It is passed by value, not by reference. The problem there is IFTI thinking you want to pass the fixed array as a fixed array and not as a slice. 2. a container is not a range. An Array is a container, so you must extract a range from it to use it in range-like activities. The second point seems odd, since builtin arrays are ranges, but an Array is different because it's a true reference type (changing the length from one reference alters the length of another reference) and it "owns" the memory contained within, unlike a builtin array which just references the memory. -Steve
I'm glad I came to the same conclusions on my own after perusing the docs and src files some more. I still find it a bit odd that I'm not testing whether my data structure is sorted or not, but a full-length slice of the structure. Although, I guess when I really think about it, it's not that mechanically different than passing a pair of iterators.
 But it's also easy to consider other things.  Consider a RedBlackTree
 container, should that be a range?  What happens when you popFront?
Indeed, and operations like fill and isSorted also do not make sense on such containers. However, popFront makes a world of sense for a range representing a traversal. Thanks to both Jonathon and yourself for your help
Feb 15 2011
prev sibling parent spir <denis.spir gmail.com> writes:
On 02/15/2011 03:00 PM, Steven Schveighoffer wrote:
 On Mon, 14 Feb 2011 21:18:39 -0500, jam <gr0v3er gmail.com> wrote:

 Hi all,

 Just curious as to the difference in the built-in variable length
 array vs. the std.container.Array and fixed length arrays when it
 comes to using them in functions that take Ranges.

 For instance the following does not compile:

 import std.algorithm;
 import std.stdio;
 import std.range;
 import std.conv;
 import std.container;
 import std.array;

 void main() {

 int[5] builtin_fixed;
 int[] builtin_variable;
 Array!(int) con_array;

 con_array.length(5);
 builtin_variable.length = 5;

 fill(builtin_variable, 9); //ok, no error
 isSorted(builtin_variable); //ditto

 //The following 4 statements produce errors
 fill(builtin_fixed, 9);
 fill(con_array, 9);

 isSorted(con_array);
 isSorted(builtin_fixed);

 }

 The errors are variations on:

 Error: template std.algorithm.fill(Range,Value) if
 (isForwardRange!(Range) && is(typeof(range.front = filler))) does not
 match any function template declaration
 Error: template std.algorithm.fill(Range,Value) if
 (isForwardRange!(Range) && is(typeof(range.front = filler))) cannot
 deduce template function from argument types !()(int[5LU],int)

 If I change those 4 statements to:

 fill(builtin_fixed[], 9);
 fill(con_array[], 9);

 isSorted(con_array[]);
 isSorted(builtin_fixed[]);

 effectively passing ranges (std.container.Array!(int).Array.Range in
 the case of con_array, and int[] for builtin_fixed) which then works
 as expected. This all makes sense, and it's easy enough to write
 wrappers, but I would (well and I did) expect the first way to just
 work. This may just be a nitpick I guess, but being new to the
 language this little detour involved quite a bit of time research (not
 a bad thing, I did learn quite a bit in the process), but makes me
 wonder if I am missing something fundamental regarding when I should
 be using these different array types.
This is because: 1. a fixed-sized array is not a range. It is passed by value, not by reference. The problem there is IFTI thinking you want to pass the fixed array as a fixed array and not as a slice. 2. a container is not a range. An Array is a container, so you must extract a range from it to use it in range-like activities. The second point seems odd, since builtin arrays are ranges, but an Array is different because it's a true reference type (changing the length from one reference alters the length of another reference) and it "owns" the memory contained within, unlike a builtin array which just references the memory. But it's also easy to consider other things. Consider a RedBlackTree container, should that be a range? What happens when you popFront?
Isn't there a step to pass toward integration of builtin collections and ranges. Should be transparent from the language-side. There are still, I guess, far too many implementation details leaking into language semantics. Python is a model for that. Denis -- _________________ vita es estrany spir.wikidot.com
Feb 15 2011