Thread overview
each & opApply
May 23, 2018
Alex
May 23, 2018
Ali Çehreli
May 23, 2018
Alex
May 23, 2018
Alex
May 23, 2018
Alex
May 23, 2018
This is a question is about usage of
´each´
https://dlang.org/phobos/std_algorithm_iteration.html#each

with a type where different opApply overloads are defined. Say, I have something like this:

´´´
void main()
{
	import std.stdio : writeln;
	import std.algorithm : each;

	auto c = Container();
	
	c.arr1.length = 50;
	c.arr2.length = 5;

	c.each!((a, b) => writeln(a, b));
        //c.each!(a => writeln(a)); // why this line does not compile?
}

struct El1{}
struct El2{}

struct Container
{
	El1[] arr1;
	El2[] arr2;

	//http://ddili.org/ders/d.en/foreach_opapply.html
	int opApply(int delegate(ref El1, ref El2) operations){ assert(0); }
	int opApply(int delegate(ref El2) operations){ assert(0); }
	int opApply(int delegate(ref El1) operations){ assert(0); }
	int opApply(int delegate(ref El2, ref El1) operations){ assert(0); }
}
´´´

The compilation error on the last line in the main is:

/usr/local/opt/dmd/include/dlang/dmd/std/algorithm/iteration.d(966,21): Error: template `D main.__lambda2` cannot deduce function from argument types `!()(El1, El2)`, candidates are:
source/app.d(12,13):        `app.main.__lambda2`
source/app.d(12,6): Error: template instance `app.main.each!((a) => writeln(a)).each!(Container)` error instantiating

So... I get the idea, that ´each´ looks only on the first opApply overload, right?
Is there any possibility, to convince it to use a specific one? Say, for the last line in the main, to use the third overload of opApply?

By the way, iterating via foreach works as expected: each of

´´´
foreach(El1 el; c){}
foreach(El2 el; c){}
foreach(El1 el1, El2 el2; c){}
foreach(El2 el1, El1 el2; c){}
´´´

compiles and iterates as it should.
May 23, 2018
On 5/23/18 9:37 AM, Alex wrote:
> This is a question is about usage of
> ´each´
> https://dlang.org/phobos/std_algorithm_iteration.html#each
> 
> with a type where different opApply overloads are defined. Say, I have something like this:
> 
> ´´´
> void main()
> {
>      import std.stdio : writeln;
>      import std.algorithm : each;
> 
>      auto c = Container();
> 
>      c.arr1.length = 50;
>      c.arr2.length = 5;
> 
>      c.each!((a, b) => writeln(a, b));
>          //c.each!(a => writeln(a)); // why this line does not compile?
> }
> 
> struct El1{}
> struct El2{}
> 
> struct Container
> {
>      El1[] arr1;
>      El2[] arr2;
> 
>      //http://ddili.org/ders/d.en/foreach_opapply.html
>      int opApply(int delegate(ref El1, ref El2) operations){ assert(0); }
>      int opApply(int delegate(ref El2) operations){ assert(0); }
>      int opApply(int delegate(ref El1) operations){ assert(0); }
>      int opApply(int delegate(ref El2, ref El1) operations){ assert(0); }
> }
> ´´´
> 
> The compilation error on the last line in the main is:
> 
> /usr/local/opt/dmd/include/dlang/dmd/std/algorithm/iteration.d(966,21): Error: template `D main.__lambda2` cannot deduce function from argument types `!()(El1, El2)`, candidates are:
> source/app.d(12,13):        `app.main.__lambda2`
> source/app.d(12,6): Error: template instance `app.main.each!((a) => writeln(a)).each!(Container)` error instantiating
> 
> So... I get the idea, that ´each´ looks only on the first opApply overload, right?

Apparently, but that's not very good. IMO, it should use the same rules as foreach. In which case, BOTH lines should fail to compile.

> Is there any possibility, to convince it to use a specific one? Say, for the last line in the main, to use the third overload of opApply?
> 
> By the way, iterating via foreach works as expected: each of
> 
> ´´´
> foreach(El1 el; c){}
> foreach(El2 el; c){}
> foreach(El1 el1, El2 el2; c){}
> foreach(El2 el1, El1 el2; c){}
> ´´´
> 
> compiles and iterates as it should.

Right, but not foreach(el1, el2; c), which is the equivalent of your each call.

-Steve
May 23, 2018
On 05/23/2018 06:49 AM, Steven Schveighoffer wrote:

> Apparently, but that's not very good. IMO, it should use the same rules as foreach. In which case, BOTH lines should fail to compile.

> -Steve

I think this is a compiler bug (limitation), which I think has been reported already (or similar ones where definition order matters). The outcome is different when one reorders the opCall definitions. It looks like only the first one is successful.

Ali
May 23, 2018
On Wednesday, 23 May 2018 at 13:49:45 UTC, Steven Schveighoffer wrote:
>
> Right, but not foreach(el1, el2; c), which is the equivalent of your each call.
>
Yes. I tried this in the first place and get a compiler error. But it seemed logical to me, that if I define two opApply overloads, which both matches two arguments, then I need to specify which one I want to use. I achieved this by specifying the types inside the foreach... concisely enough for me :)

So... I'm looking how to do the same with ´each´, as defining the type of the lambda didn't help.
May 23, 2018
On 5/23/18 9:59 AM, Alex wrote:
> On Wednesday, 23 May 2018 at 13:49:45 UTC, Steven Schveighoffer wrote:
>>
>> Right, but not foreach(el1, el2; c), which is the equivalent of your each call.
>>
> Yes. I tried this in the first place and get a compiler error. But it seemed logical to me, that if I define two opApply overloads, which both matches two arguments, then I need to specify which one I want to use. I achieved this by specifying the types inside the foreach... concisely enough for me :)
> 
> So... I'm looking how to do the same with ´each´, as defining the type of the lambda didn't help.

In your example, you did not define the types for the lambda (you used (a, b) => writeln(a, b) ). But I suspect `each` is not going to work even if you did. In essence, `each` does not know what the lambda requires, especially if it is a typeless lambda. So it essentially needs to replicate what foreach would do -- try each of the overloads, and if one matches, use it, if none or more than one matches, fail.

I suspect it's more complex, and I'm not sure that it can be done with the current tools. But it's definitely a bug that it doesn't work when you specify the types.

-Steve
May 23, 2018
On Wednesday, 23 May 2018 at 14:19:31 UTC, Steven Schveighoffer wrote:
> On 5/23/18 9:59 AM, Alex wrote:
>> On Wednesday, 23 May 2018 at 13:49:45 UTC, Steven Schveighoffer wrote:
>>>
>>> Right, but not foreach(el1, el2; c), which is the equivalent of your each call.
>>>
>> Yes. I tried this in the first place and get a compiler error. But it seemed logical to me, that if I define two opApply overloads, which both matches two arguments, then I need to specify which one I want to use. I achieved this by specifying the types inside the foreach... concisely enough for me :)
>> 
>> So... I'm looking how to do the same with ´each´, as defining the type of the lambda didn't help.
>
> In your example, you did not define the types for the lambda (you used (a, b) => writeln(a, b) ). But I suspect `each` is not going to work even if you did.

Yep. Tried this...

> In essence, `each` does not know what the lambda requires, especially if it is a typeless lambda. So it essentially needs to replicate what foreach would do -- try each of the overloads, and if one matches, use it, if none or more than one matches, fail.
>
> I suspect it's more complex, and I'm not sure that it can be done with the current tools. But it's definitely a bug that it doesn't work when you specify the types.
>
Ah... ok. Then, let me file a bug...

May 23, 2018
On Wednesday, 23 May 2018 at 14:24:18 UTC, Alex wrote:
> Ah... ok. Then, let me file a bug...

Bug filed.
https://issues.dlang.org/show_bug.cgi?id=18898