Thread overview
Cartesian product of immutable ranges
Jan 26, 2014
matovitch
Jan 26, 2014
Stanislav Blinov
Jan 26, 2014
matovitch
Jan 26, 2014
Jakob Ovrum
Jan 26, 2014
matovitch
Jan 26, 2014
matovitch
Jan 27, 2014
Stanislav Blinov
January 26, 2014
I posted a question on stackoverflow about the cartesian product of immutable ranges :

http://stackoverflow.com/questions/21368501/cartesian-product-of-immutable-ranges

There are a lot of various thing I don't understand.

This code compiles and run :

import std.stdio;
import std.range;
import std.algorithm;

void main() {

    immutable int[] B = [ 1, 2, 3 ];
    int[] C = [ 4, 5, 6 ];
    auto BC = cartesianProduct(B, C);
    writeln(BC);
}

This one doesn't :

import std.stdio;
import std.range;
import std.algorithm;

void main() {

    int[] B = [ 1, 2, 3 ];
    immutable int[] C = [ 4, 5, 6 ];
    auto BC = cartesianProduct(B, C);
    writeln(BC);
}

And this one does :

import std.stdio;
import std.range;
import std.algorithm;

void main() {

    immutable int[] B = [ 1, 2, 3 ];
    immutable int[] C = [ 4, 5, 6 ];
    auto BC = zip(B, C);
    writeln(BC);
}

Here B and C aren't inputRange thought acording to the template constraint of zip there should be. How can it compiles ? How to compute the cartesian product of two immutable ranges ?
January 26, 2014
On Sunday, 26 January 2014 at 21:49:37 UTC, matovitch wrote:

> void main() {
>
>     immutable int[] B = [ 1, 2, 3 ];
>     immutable int[] C = [ 4, 5, 6 ];
>     auto BC = zip(B, C);
>     writeln(BC);
> }
>
> Here B and C aren't inputRange thought acording to the template constraint of zip there should be. How can it compiles ?

B and C aren't, but B[] and C[] are. That's what's going on when
you pass an array as function argument: a full slice is taken.
See for yourself:

void foo(R)(R r) {
     writeln(R.stringof);
}

foo(B); // will print immutable(int)[]

> How to compute the cartesian product of two immutable ranges ?

Short answer: with std.algorithm - you can't. Because internally
it (Zip, actually) performs assignments, and you can't assign to
immutable(T).

Why?
January 26, 2014
On Sunday, 26 January 2014 at 22:19:47 UTC, Stanislav Blinov wrote:
> On Sunday, 26 January 2014 at 21:49:37 UTC, matovitch wrote:
>
>> void main() {
>>
>>    immutable int[] B = [ 1, 2, 3 ];
>>    immutable int[] C = [ 4, 5, 6 ];
>>    auto BC = zip(B, C);
>>    writeln(BC);
>> }
>>
>> Here B and C aren't inputRange thought acording to the template constraint of zip there should be. How can it compiles ?
>
> B and C aren't, but B[] and C[] are. That's what's going on when
> you pass an array as function argument: a full slice is taken.
> See for yourself:
>
> void foo(R)(R r) {
>      writeln(R.stringof);
> }
>
> foo(B); // will print immutable(int)[]
>

You mean that two input ranges are created from the immutable arrays when I call the function ?

Zip doesn't compiles while zip compile. :/

Here is the implementation of zip :

auto zip(Ranges...)(Ranges ranges)
    if (Ranges.length && allSatisfy!(isInputRange, Ranges))
{
    return Zip!Ranges(ranges);
}

January 26, 2014
On Sunday, 26 January 2014 at 22:32:32 UTC, matovitch wrote:
> You mean that two input ranges are created from the immutable arrays when I call the function ?
>
> Zip doesn't compiles while zip compile. :/

`Zip` must be explicitly instantiated, which allows you to attempt to instantiate it with head-immutable slices, which isn't allowed because head-immutable slices aren't input ranges due to a lack of working `popFront()`.

> Here is the implementation of zip :
>
> auto zip(Ranges...)(Ranges ranges)
>     if (Ranges.length && allSatisfy!(isInputRange, Ranges))
> {
>     return Zip!Ranges(ranges);
> }

`zip` is a range constructor function. These are used to allow IFTI (Implicit Function Template Instantiation) to infer template arguments, so the user doesn't have to provide them explicitly.

With IFTI, head-immutable and head-const are stripped from slice types, so even though you pass immutable(int[]), the parameter type is set to be immutable(int)[], i.e. an mutable slice of immutable integers. Only the latter is a valid input range.
January 26, 2014
On Sunday, 26 January 2014 at 22:52:56 UTC, Jakob Ovrum wrote:
> On Sunday, 26 January 2014 at 22:32:32 UTC, matovitch wrote:
>> You mean that two input ranges are created from the immutable arrays when I call the function ?
>>
>> Zip doesn't compiles while zip compile. :/
>
> `Zip` must be explicitly instantiated, which allows you to attempt to instantiate it with head-immutable slices, which isn't allowed because head-immutable slices aren't input ranges due to a lack of working `popFront()`.
>
>> Here is the implementation of zip :
>>
>> auto zip(Ranges...)(Ranges ranges)
>>    if (Ranges.length && allSatisfy!(isInputRange, Ranges))
>> {
>>    return Zip!Ranges(ranges);
>> }
>
> `zip` is a range constructor function. These are used to allow IFTI (Implicit Function Template Instantiation) to infer template arguments, so the user doesn't have to provide them explicitly.
>
> With IFTI, head-immutable and head-const are stripped from slice types, so even though you pass immutable(int[]), the parameter type is set to be immutable(int)[], i.e. an mutable slice of immutable integers. Only the latter is a valid input range.

Thank you very much for these very clear explanations ! I am quite tired tonight and shouldn't have started to code in the first place anyway. ;-)

January 26, 2014
On Sunday, 26 January 2014 at 22:19:47 UTC, Stanislav Blinov wrote:
> On Sunday, 26 January 2014 at 21:49:37 UTC, matovitch wrote:
>
> Short answer: with std.algorithm - you can't. Because internally
> it (Zip, actually) performs assignments, and you can't assign to
> immutable(T).
>
> Why?

Here is the problem...

import std.stdio;
import std.range;
import std.algorithm;

void main() {
    writeln(zip([1,2,4,3], take(Repeat!(immutable(int))(2), 4)));
}

With the current implementation the second order immutable should be stripped...
January 27, 2014
You might want to consider submitting a bug report on account of cartesianProduct() not being able to deal with ranges of immutable elements. The worst that can happen is that you'd get a real expert's explanation on why it's not a bug. But who knows, perhaps it is :)