www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Non-consistent implicit function template specializations

reply Rekel <paultjeadriaanse gmail.com> writes:
When using implicit function templates, identical specialization 
yield different results.
Example:

```d



void foo(T)(){
	writeln("1");
}

void foo(T : T[])(){
	writeln("1");
}

void main(string[] args) { // Works
	TFoo!(int).foo();            // "1"
	TFoo!(double[]).foo();       // "2"
	foo!(int)();            // "1"
	foo!(double[])();       // "1" !
}
```

I'm fairly certain the last call _should_ yield "2", yet it does 
not.
Should I submit a bugreport? And will that even help? There's 
even 'NEW' bugs from 2016 :'c

---

Interestingly enough, it seems I've made a previous thread about 
this in January 
(https://forum.dlang.org/post/zudtiruaxdfdwcjennmr forum.dlang.org), though in
it I state `TFoo!(uint[]) yields the array version`, which as can be seen from
the example above, is not or no longer true.
Sadly I'm having issues with template arguments more often 
(https://forum.dlang.org/post/xkgpheuhohbffzgdbqhb forum.dlang.org)
And I sadly don't see any improvement to this happening any time 
in the future. Even bugs don't seem to get fixed in any timely 
manner (Not meant as an insult, just being realistic :/).
Aug 17 2021
next sibling parent reply Mike Parker <aldacron gmail.com> writes:
On Tuesday, 17 August 2021 at 09:59:53 UTC, Rekel wrote:
 When using implicit function templates, identical 
 specialization yield different results.
 Example:

 ```d



 void foo(T)(){
 	writeln("1");
 }

 void foo(T : T[])(){
 	writeln("1");
 }

 void main(string[] args) { // Works
 	TFoo!(int).foo();            // "1"
 	TFoo!(double[]).foo();       // "2"
 	foo!(int)();            // "1"
 	foo!(double[])();       // "1" !
 }
 ```

 I'm fairly certain the last call _should_ yield "2", yet it 
 does not.
The error is in your code. Both of your `foo` templates are implemented to print `"1"`. Change the second one to print "2" and you will see the desired output. At any rate, to specialize on arrays you should generally be using something like `T : U[], U`. Ditto for pointers: `T : U*, U`.
Aug 17 2021
parent Rekel <paultjeadriaanse gmail.com> writes:
On Tuesday, 17 August 2021 at 10:14:07 UTC, Mike Parker wrote:
 The error is in your code. Both of your `foo` templates are 
 implemented to print `"1"`. Change the second one to print "2" 
 and you will see the desired output.
I keep managing to disappoint myself greatly... this is absurd, so sorry.
Aug 17 2021
prev sibling next sibling parent reply Mike Parker <aldacron gmail.com> writes:
On Tuesday, 17 August 2021 at 09:59:53 UTC, Rekel wrote:

 time in the future. Even bugs don't seem to get fixed in any 
 timely manner (Not meant as an insult, just being realistic :/).
We do have a paid Issue/Pull-Request manager now (Razvan Nitu), and he's prioritizing issues for strike teams composed of volunteers willing to fix them. If you find a specific bug that is a blocker or a major headache, make a post about it here in the forums. Sometimes, the reason a bug hasn't been fixed is simply that it hasn't caught the attention of the right person. With Razvan in place, it's more likely such issues will be resolved, or at least moved up on the priority list, if you let him know about them. A forum post is an easy way to do that while also bringing it to the attention of others who may be looking for something to work on.
Aug 17 2021
parent Rekel <paultjeadriaanse gmail.com> writes:
On Tuesday, 17 August 2021 at 10:21:39 UTC, Mike Parker wrote:
 We do have a paid Issue/Pull-Request manager now (Razvan Nitu), 
 and he's prioritizing issues for strike teams composed of 
 volunteers willing to fix them. If you find a specific bug that 
 is a blocker or a major headache, make a post about it here in 
 the forums.

 Sometimes, the reason a bug hasn't been fixed is simply that it 
 hasn't caught the attention of the right person. With Razvan in 
 place, it's more likely such issues will be resolved, or at 
 least moved up on the priority list, if you let him know about 
 them. A forum post is an easy way to do that while also 
 bringing it to the attention of others who may be looking for 
 something to work on.
That's great to hear :), I'll keep it in mind.
Aug 17 2021
prev sibling next sibling parent reply Rekel <paultjeadriaanse gmail.com> writes:
As my post was not the actual cause of my issue (my apology for 
the mistake), I think I have found the actual reason I'm 
currently having problems.
This seems to be related to a (seeming, I might be wrong) 
inability to specialize over both 1d and 2d arrays separately. 
(If constraining the length to values.)

This brings me to 2 questions.
1. How does one specialize a template for both 1d and 2d arrays 
at the same time?
2. Are there any plans to rework templates? like:
- Making `template TFoo(T : T[])` illegal. This makes little 
sense to me however I look at it, and as several people have 
advised against it it's confusing it's in the docs. (21.4.1)
- Changing the way priorities are set using specializations in 
general, to make them less pitfall-y.
- Allow for 2+d length specification. At the moment `T[][L]` 
works while `T[L][L]` does not seem to.

```d
void foo(T)(T a){...}

void foo(T:U[L], uint L)(T a){...}
void foo(T:U[L][L], uint L)(T a){...} // Never matched
void foo(T:U[L], U:V[L], V uint L)(T a){...} // Never matched 
(alternatively)

// Alternative workaround, very cluttery but with more control.
void foo(T)(T a) if(!is(typeof(a[0]))&&!is(typeof(a[0][0]))) {...}
void foo(T, uint L)(T[L] a) if(!is(typeof(a[0]))){...}
void foo(T, uint L)(T[L][L] a) {...} // Still does not work.
void foo(T, unit L(T[][L] a) {static foreach(i;0..L) 
assert(a[i].length==L); ...} // Will (generally) work but may 
include runtime checking & is generally much less preferrable.

// Should work with
void main(string[] args) {
	foo([[1],[2]]);
	foo([1,2]);
	foo(1);
}
```

- Supporting specializations such as the following (from 
https://forum.dlang.org/post/kdgfwlydkgmwvzrieyek forum.dlang.org):
```d
void foo(L, T, uint S)(L l, T[S] r){
	writeln("foo");
}

void bar(L, R:T[S], T, uint S)(L l, R r){
	writeln("bar");
}

void main(string[] args) {
	foo(1, [1,2,3,4]); // "foo"
	bar(1, [1,2,3,4]); // "cannot deduce function"
}
```
I'm actually still not sure why this shouldn't work, but changing 
bar to foo isn't too difficult. (though it's more preferrable to 
work regardless, it currently feels like a pitfall)

---

Not sure if I'm asking the right questions, hope it's not of 
nuisance.
- Rekel
Aug 17 2021
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 8/17/21 10:20 AM, Rekel wrote:
 As my post was not the actual cause of my issue (my apology for the 
 mistake), I think I have found the actual reason I'm currently having 
 problems.
 This seems to be related to a (seeming, I might be wrong) inability to 
 specialize over both 1d and 2d arrays separately. (If constraining the 
 length to values.)
 
 This brings me to 2 questions.
 1. How does one specialize a template for both 1d and 2d arrays at the 
 same time?
 2. Are there any plans to rework templates? like:
 - Making `template TFoo(T : T[])` illegal. This makes little sense to me 
 however I look at it, and as several people have advised against it it's 
 confusing it's in the docs. (21.4.1)
 - Changing the way priorities are set using specializations in general, 
 to make them less pitfall-y.
 - Allow for 2+d length specification. At the moment `T[][L]` works while 
 `T[L][L]` does not seem to.
You should try out things individually to see if they at least work.
 
 ```d
 void foo(T)(T a){...}
 
 void foo(T:U[L], uint L)(T a){...}
This is an invalid specification, what is U? Did you mean: void foo(T: U[L], U, uint L)(T a) {...}
 void foo(T:U[L][L], uint L)(T a){...} // Never matched
Apart from another missing U, this is only a SQUARE 2d-array (both dimensions the same), your example below only calls with a 1x2 array.
 void foo(T:U[L], U:V[L], V uint L)(T a){...} // Never matched 
 (alternatively)
I don't think you need this, and I had to comment it out, or the compiler wouldn't build.
 
 // Should work with
 void main(string[] args) {
      foo([[1],[2]]);
      foo([1,2]);
      foo(1);
 }
 ```
All these are calling with array literals, which default to dynamic arrays, not static arrays. -Steve
Aug 17 2021
parent reply Rekel <paultjeadriaanse gmail.com> writes:
On Tuesday, 17 August 2021 at 16:24:38 UTC, Steven Schveighoffer 
wrote:
 void foo(T:U[L], uint L)(T a){...}
This is an invalid specification, what is U? Did you mean:
Yes, sorry typo in the forum.
 void foo(T: U[L], U, uint L)(T a) {...}

 void foo(T:U[L][L], uint L)(T a){...} // Never matched
Apart from another missing U, this is only a SQUARE 2d-array (both dimensions the same), your example below only calls with a 1x2 array.
Again, sorry a typo, calling with `[[1,2],[3,4]]`.
 void foo(T:U[L], U:V[L], V uint L)(T a){...} // Never matched 
 (alternatively)
I don't think you need this, and I had to comment it out, or the compiler wouldn't build.
That is correct, it's equivalent thus causes 2 matches.
 All these are calling with array literals, which default to 
 dynamic arrays, not static arrays.
I realise that is their default, though in this scenario they should (I believe) be used as static arrays. (This works for me in any case)
Aug 17 2021
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 8/17/21 2:07 PM, Rekel wrote:
 On Tuesday, 17 August 2021 at 16:24:38 UTC, Steven Schveighoffer wrote:
 All these are calling with array literals, which default to dynamic 
 arrays, not static arrays.
I realise that is their default, though in this scenario they should (I believe) be used as static arrays. (This works for me in any case)
According to my tests, it prefers the `T` version over the static array version. Which leads me to believe that it prefers a dynamic array over a static one. In fact, if I comment out the `T` version, it doesn't compile. It literally will not pick that specialization, even if it can interpret the literal that way. which is really bizarre, since if you do it without specializations, but just spelling out all the template components (as in your alternative workaround), it WILL pick that one over a dynamic array one. -Steve
Aug 17 2021
parent Rekel <paultjeadriaanse gmail.com> writes:
On Tuesday, 17 August 2021 at 18:27:21 UTC, Steven Schveighoffer 
wrote:
 According to my tests, it prefers the `T` version over the 
 static array version. Which leads me to believe that it prefers 
 a dynamic array over a static one. In fact, if I comment out 
 the `T` version, it doesn't compile. It literally will not pick 
 that specialization, even if it can interpret the literal that 
 way.

 which is really bizarre, since if you do it without 
 specializations, but just spelling out all the template 
 components (as in your alternative workaround), it WILL pick 
 that one over a dynamic array one.
Oh my, that's weird... Not meant to bash but given all I've seen of argument deduction & templates & specializations... I think the implementation needs some serious rework 😅 --- Interestingly enough my approach will not even work for 2d array literals. It will manage going to int[][2], but int[2][2] is one step too far. Which is a real bummer. :( That is, it won't figure it out itself, but when you call `foo(T, uintL)(T[L][L]...` using an explicit `foo!(int, 2)` it _will_ work. Even though it manages T[L] just fine. Meanwhile T[2][L] _and_ T[L][2] won't work when called with a 2x2 array literal.
Aug 18 2021
prev sibling parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 8/17/21 2:59 AM, Rekel wrote:



I don't have such problems because I am not smart enough to understand that syntax so I don't use it. :) I use template constraints (which have other problems). import std.traits; import std.stdio; template TFoo(T) if (!isArray!T) { void foo(){ writeln("not array"); } } template TFoo(T) if (isArray!T) { void foo(){ writeln("array"); } } void main() { TFoo!(int).foo(); TFoo!(int[]).foo(); } If you want 2 dimensional arrays, then you can use import std.range; isArray!T && (isArray!(ElementType!T)) Ali
Aug 17 2021
parent reply Rekel <paultjeadriaanse gmail.com> writes:
On Tuesday, 17 August 2021 at 18:46:05 UTC, Ali Çehreli wrote:
 I don't have such problems because I am not smart enough to 
 understand that syntax so I don't use it. :) I use template 
 constraints (which have other problems).
Yeah, they seem to be a bit more trustworthy to some extent.
 If you want 2 dimensional arrays, then you can use

 import std.range;

   isArray!T && (isArray!(ElementType!T))
I tried looking into how isArray is defined. Like, does being able to index mean it's an array, or are these only static &/or dynamic arrays? Though I couldn't understand the sourcecode. Hence I just use(d) `is(typeof(variable[0]))` and `is(typeof(variable[0][0]))`.
Aug 18 2021
next sibling parent reply Paul Backus <snarwin gmail.com> writes:
On Wednesday, 18 August 2021 at 11:10:49 UTC, Rekel wrote:
 I tried looking into how isArray is defined. Like, does being 
 able to index mean it's an array, or are these only static &/or 
 dynamic arrays?
Did you read the documentation? https://phobos.dpldocs.info/std.traits.isArray.html
Aug 18 2021
parent Rekel <paultjeadriaanse gmail.com> writes:
On Wednesday, 18 August 2021 at 13:35:07 UTC, Paul Backus wrote:
 On Wednesday, 18 August 2021 at 11:10:49 UTC, Rekel wrote:
 I tried looking into how isArray is defined. Like, does being 
 able to index mean it's an array, or are these only static 
 &/or dynamic arrays?
Did you read the documentation? https://phobos.dpldocs.info/std.traits.isArray.html
Ah it's specifically static & dynamic arrays, I see.
Aug 18 2021
prev sibling parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 8/18/21 4:10 AM, Rekel wrote:

   isArray!T && (isArray!(ElementType!T))
I tried looking into how isArray is defined. Like, does being able to index mean it's an array, or are these only static &/or dynamic arrays?
The definitions are in phobos/std/traits.d enum bool isArray(T) = isStaticArray!T || isDynamicArray!T; isStaticArray uses the compiler's __traits feature: enum bool isStaticArray(T) = __traits(isStaticArray, T); isDynamicArray uses the is expression but apparently has some history: template isDynamicArray(T) { static if (is(T == U[], U)) enum bool isDynamicArray = true; else static if (is(T U == enum)) // BUG: isDynamicArray / isStaticArray considers enums // with appropriate base types as dynamic/static arrays // Retain old behaviour for now, see // https://github.com/dlang/phobos/pull/7574 enum bool isDynamicArray = isDynamicArray!U; else enum bool isDynamicArray = false; } Ali
Aug 18 2021