phobos icon indicating copy to clipboard operation
phobos copied to clipboard

Cannot call `std.range.chain` with ranges of range of same ultimate element type

Open Geod24 opened this issue 5 months ago • 5 comments

import std.algorithm : map;
import std.range : chain, front, iota, ElementType;

immutable string Letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

void main () {
    // Range of string
    auto str = iota(20).map!(v => Letters[v .. $]);
    // Range of range of string
    auto ror = iota(10).map!(v => str.save);
    string[][] equiv = [ ["A", "B"], ["C", "D"] ];
    pragma(msg, ElementType!(typeof(ror)), " -- ", ElementType!(typeof(equi\
v)));
    pragma(msg, ElementType!(typeof(ror.front)), " -- ", ElementType!(typeo\
f(equiv.front)));
    auto result = chain(ror, equiv);
}

Yields the error:

% ldc2 ror.d
MapResult!(__lambda_L8_C30, Result) -- string[]
string -- string
ror.d(14): Error: template `chain` is not callable using argument types `!()(MapResult!(__lambda_L10_C30, Result), string[][])`
    auto result = chain(ror, equiv);
                       ^
/home/mlang/dlang/ldc-1.41.0/bin/../import/std/range/package.d(977):        Candidate is: `chain(Ranges...)(Ranges rs)`
  with `Ranges = (MapResult!(__lambda_L10_C30, Result), string[][])`
  must satisfy the following constraint:
`       !is(CommonType!(staticMap!(ElementType, staticMap!(Unqual, Ranges))) == void)`
auto chain(Ranges...)(Ranges rs)
     ^

I expect it to work, since ror is essentially a range of string, but the CommonType check seems to be too restrictive for this.

Geod24 avatar Jun 24 '25 08:06 Geod24

What return type do you expect chain(ror, equiv).front to have in this scenario?

pbackus avatar Jun 24 '25 09:06 pbackus

Something that is a range of strings.

Geod24 avatar Jun 24 '25 13:06 Geod24

This would be very complicated to do. f we're expecting ranges of ranges to work, I quess we should to support ranges of any dimensionality.

I believe the return type of chain(ror, equiv).front would be ChooseResult!(typeof(ror.front), typeof(equiv.front)). But then what would it be for chain(roror, equiv).front? I'm already not sure, let alone if we add even more dimensions.

dukc avatar Sep 03 '25 16:09 dukc

I dont think this should work with default chain; there should be more effort to not break sensible return types

This should only work if sumtypes in general are supported; which would look like this:

import std;
immutable string Letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

auto sumtypechain(R1,R2)(R1 r1,R2 r2){
	struct range{
		R1 r1;R2 r2;
		alias E=SumType!(ElementType!R1,ElementType!R2);
		auto front()=>r1.empty?E(r2.front):E(r1.front);
		void popFront(){
			if( ! r1.empty){
				r1.popFront;
			} else {
				r2.popFront;
		}}
		bool empty()=>r1.empty&&r2.empty;
	}
	return range(r1,r2);
}
unittest{
	sumtypechain("abc",[1,2,3]).writeln;
}

unittest{
	// Range of string
	auto str = iota(20).map!(v => Letters[v .. $]);
	// Range of range of string
	auto ror = iota(10).map!(v => str.save);
	string[][] equiv = [ ["A", "B"], ["C", "D"] ];
	pragma(msg, ElementType!(typeof(ror)), " -- ", ElementType!(typeof(equiv)));
	pragma(msg, ElementType!(typeof(ror.front)), " -- ", ElementType!(typeof(equiv.front)));
	auto result = sumtypechain(ror, equiv);
	result.writeln;
}

crazymonkyyy avatar Sep 03 '25 17:09 crazymonkyyy

auto mapchain(alias F,Rs...)(Rs rs){    
    return mixin((){
        string output="chain(";
        foreach(I;0..Rs.length){
            output~="rs["~I.to!string~"].map!F,";
        }
        output~=")";
        return output;
    }());
}

auto sumtypechain(Rs...)(Rs rs){
    //alias E=SumType!(staticMap!(ElementType,Rs));
    alias E=SumType!(NoDuplicates!(staticMap!(ElementType,Rs)));//snar this requirment is bad design
    return mapchain!(a=>E(a))(rs);
}

unittest{
    sumtypechain("abc",[1,2,3],iota(0,100),[13.37,4.20]).writeln;
}

another idea; I think you could justify "mapchain"

crazymonkyyy avatar Sep 03 '25 19:09 crazymonkyyy