Thread overview
Array types not treated uniformly when passed as ranges
Feb 15, 2011
jam
Feb 15, 2011
jam
Feb 15, 2011
spir
February 15, 2011
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
February 15, 2011
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
February 15, 2011
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
February 15, 2011
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