www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - each & opApply

reply Alex <sascha.orlov gmail.com> writes:
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
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
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
next sibling parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
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
prev sibling parent reply Alex <sascha.orlov gmail.com> writes:
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
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
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
parent reply Alex <sascha.orlov gmail.com> writes:
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
parent Alex <sascha.orlov gmail.com> writes:
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
May 23 2018