digitalmars.D - [DIP] Resolution of Alias Template Parameters in Function Templates
- Stefanos Baziotis (35/35) May 18 2019 Here is a DIP that I've been working the last couple of days:
- jmh530 (42/77) May 18 2019 Thanks for working on this.
- Stefanos Baziotis (29/40) May 18 2019 Good point, this is probably the least edited part of the code.
- jmh530 (9/36) May 20 2019 Hmm, the link to the DIP isn't working for me now.
- jmh530 (14/16) May 20 2019 Ah, found it again
- Stefanos Baziotis (13/24) May 20 2019 I agree with you, but there might be more to it than it meets the
- Stefanos Baziotis (38/45) May 20 2019 Yes, I updated it yesterday, please use this link:
- jmh530 (8/21) May 20 2019 Yes, that's what you would expect to happen with a normal alias.
- Stefanos Baziotis (5/11) May 20 2019 But with the above reasoning we want this to fail (that is,
- jmh530 (3/8) May 20 2019 Sorry, it would be true for Num!int, but false for Num!float or
- jmh530 (5/14) May 20 2019 It makes more sense if I said it should evaluate is(T==int) in
- Stefanos Baziotis (24/40) May 20 2019 If the second static if is `if(is(T == float))` and we passed
- jmh530 (3/4) May 20 2019 Sorry, I was getting confused. I was thinking the Num was from a
- Stefanos Baziotis (2/6) May 20 2019 It's ok, thank you for the feedback!
- Nick Treleaven (4/4) May 20 2019 On Saturday, 18 May 2019 at 12:32:13 UTC, Stefanos Baziotis wrote:
- Stefanos Baziotis (2/7) May 20 2019 Yes, thank you, I updated it just yesterday and forgot it.
Here is a DIP that I've been working the last couple of days: https://github.com/baziotis/DIPs/blob/master/DIPs/DIP1021.md It is a proposal about how to handle alias templates when they appear as parameters in function templates. It was initially proposed by Ilya here: https://forum.dlang.org/post/kvcrsoqozrflxibgxtlo forum.dlang.org as a GSoC project and then again by Seb as part of the DataFrame project: https://forum.dlang.org/post/jyzgzxqgaggltgifwnxx forum.dlang.org There are also a couple of Bugzilla reports, 2 of those referenced in the DIP. I tried to make it as much of a scientific paper as possible, but note that I don't have experience either in writing papers or DIPs. The DIP references a PR with a 3-day prototype implementation. That was an initial implementation that I was working before identifying all the corner cases of the feature. And the corner cases make it such that probably a lot of time has to be spent for this feature to be fully supported. I believe however that it might come in handy as it handles a lot of the Slice cases, which are the primal motivation behind this. This is not my main GSoC project so I won't have to work on that on the summer, but if anyone wants to work on it, please keep me posted. The DIP also references an article that explains the problem in a more informal fashion style. I'm planning to change this article to be more implementation-specific where I will analyze the most important problems of implementing this fully. Best, Stefanos
May 18 2019
On Saturday, 18 May 2019 at 12:32:13 UTC, Stefanos Baziotis wrote:Here is a DIP that I've been working the last couple of days: https://github.com/baziotis/DIPs/blob/master/DIPs/DIP1021.md It is a proposal about how to handle alias templates when they appear as parameters in function templates. It was initially proposed by Ilya here: https://forum.dlang.org/post/kvcrsoqozrflxibgxtlo forum.dlang.org as a GSoC project and then again by Seb as part of the DataFrame project: https://forum.dlang.org/post/jyzgzxqgaggltgifwnxx forum.dlang.org There are also a couple of Bugzilla reports, 2 of those referenced in the DIP. I tried to make it as much of a scientific paper as possible, but note that I don't have experience either in writing papers or DIPs. The DIP references a PR with a 3-day prototype implementation. That was an initial implementation that I was working before identifying all the corner cases of the feature. And the corner cases make it such that probably a lot of time has to be spent for this feature to be fully supported. I believe however that it might come in handy as it handles a lot of the Slice cases, which are the primal motivation behind this. This is not my main GSoC project so I won't have to work on that on the summer, but if anyone wants to work on it, please keep me posted. The DIP also references an article that explains the problem in a more informal fashion style. I'm planning to change this article to be more implementation-specific where I will analyze the most important problems of implementing this fully. Best, StefanosThanks for working on this. You might remove the line "People seem to pay money for this feature to be implemented." While people (like Ilya and me) are willing to pay for this, it's not really relevant to the technical issues. Also, the forum has been discussing recently using static ifs within a function instead of function overloads. I think the same argument applies here. For instance, if switching to the commented code, then the writeln("here") will get skipped for writeln("there"). Correspondingly, I think it would be nice for the DIP to also discuss resolving is expressions as well. struct Slice(T) { } struct StairsIterator(T, string direction) { } alias PackedUpperTriangularMatrix(T) = Slice!(StairsIterator!(T*, "-")); auto foo(T)(T m) { import std.stdio : writeln; static if (is(T : Slice!(StairsIterator!(U*, "-")), U)) { //static if (is(T : PackedUpperTriangularMatrix!U, U)) { writeln("here"); } else static if (is(T : Slice!U, U)) { writeln("there"); } else { static assert(0, "should not be here"); } } void main() { Slice!int x; PackedUpperTriangularMatrix!int y; foo(x); foo(y); import std.stdio : writeln; writeln(is(typeof(y) : PackedUpperTriangularMatrix!U, U)); //prints false writeln(is(typeof(y) : Slice!(StairsIterator!(U*, "-")), U)); //prints true }
May 18 2019
On Saturday, 18 May 2019 at 16:05:10 UTC, jmh530 wrote:Thanks for working on this.Your welcome.You might remove the line "People seem to pay money for this feature to be implemented." While people (like Ilya and me) are willing to pay for this, it's not really relevant to the technical issues.Good point, this is probably the least edited part of the code. It probably came from early draft versions. Removed.Also, the forum has been discussing recently using static ifs within a function instead of function overloads. I think the same argument applies here. For instance, if switching to the commented code, then the writeln("here") will get skipped for writeln("there"). Correspondingly, I think it would be nice for the DIP to also discuss resolving is expressions as well.Thanks for the information, I was not aware. But, it's not really clear what the behaviour should be. Look point 2. here [1]. What I understand is that the general idea is to make aliases more identical to what they alias rather than distinguishing them. This DIP is a step towards that. That seems to be the direction of the compiler source code as well. On the other hand, the behaviour that you would expect in the examples distinguishes them. In my opinion, I think that there should be a clear direction towards the one or the other. But of course this is not necessarily what you, the community and / or the language maintainers want. In that case, because it is a matter of opinion, it is not (fortunately) up to me to decide. We just need to listen to what most people want / care about along with some rationale backing it up. :) That is, in what cases they are different and in what they're not. This could be as simple as "only in `is` case they are different" or way more complicated. [1] https://dlang.org/spec/declaration.html#alias
May 18 2019
On Saturday, 18 May 2019 at 16:52:45 UTC, Stefanos Baziotis wrote:[snip] Thanks for the information, I was not aware. But, it's not really clear what the behaviour should be. Look point 2. here [1]. What I understand is that the general idea is to make aliases more identical to what they alias rather than distinguishing them. This DIP is a step towards that. That seems to be the direction of the compiler source code as well. On the other hand, the behaviour that you would expect in the examples distinguishes them. In my opinion, I think that there should be a clear direction towards the one or the other. But of course this is not necessarily what you, the community and / or the language maintainers want. In that case, because it is a matter of opinion, it is not (fortunately) up to me to decide. We just need to listen to what most people want / care about along with some rationale backing it up. :) That is, in what cases they are different and in what they're not. This could be as simple as "only in `is` case they are different" or way more complicated. [1] https://dlang.org/spec/declaration.html#aliasHmm, the link to the DIP isn't working for me now. My rationale would be that, from a generic code perspective, it is very helpful if alias templates behave like aliases (including both is expressions and use in functions). There may be good reasons why that shouldn't or can't be the case, but I think the goal should be on allowing for the most generic code possible. It may be useful to include in the DIP the cases where alias templates can and cannot be used like aliases...
May 20 2019
On Monday, 20 May 2019 at 14:45:59 UTC, jmh530 wrote:[snip Hmm, the link to the DIP isn't working for me now.Ah, found it again https://github.com/baziotis/DIPs/blob/master/Drafts/1NNN-SB.md I don't see that "Changing the parameter type might not be enough" caveat as being that big of an issue. I see the problem as coming from the alias alias TemplateAlias(S, V) = TemplateType!(S); There is no reason for V to be there. You could just as easily write alias TemplateAlias(S, V = null) = TemplateType!(S); However, the user may not be able to change TemplateAlias, in which case something like alias TemplateAlias(S) = TemplateAlias!(S, typeof(null)); might be an acceptable solution I think.
May 20 2019
On Monday, 20 May 2019 at 17:35:23 UTC, jmh530 wrote:I don't see that "Changing the parameter type might not be enough" caveat as being that big of an issue. I see the problem as coming from the alias alias TemplateAlias(S, V) = TemplateType!(S);I agree with you, but there might be more to it than it meets the eye because of course, it is accepted by the language currently. Other than that, to end up that this is a problem of aliases is irrelevant to the DIP. It doesn't have to do with template functions or template aliases as parameters to template functions.There is no reason for V to be there. You could just as easily write alias TemplateAlias(S, V = null) = TemplateType!(S);This still causes the drop of a template function template parameter.However, the user may not be able to change TemplateAlias, in which case something like alias TemplateAlias(S) = TemplateAlias!(S, typeof(null)); might be an acceptable solution I think.Yes, but that is how the user decides to solve it. Again, concluding about what aliases are acceptable and what not is a separate topic, which is of course important.
May 20 2019
On Monday, 20 May 2019 at 14:45:59 UTC, jmh530 wrote:Hmm, the link to the DIP isn't working for me now.Yes, I updated it yesterday, please use this link: https://github.com/baziotis/DIPs/blob/master/Drafts/1NNN-SB.mdMy rationale would be that, from a generic code perspective, it is very helpful if alias templates behave like aliases (including both is expressions and use in functions). There may be good reasons why that shouldn't or can't be the case, but I think the goal should be on allowing for the most generic code possible.I agree, but with the current proposal, they do behave like aliases in that, there is no distinction between the true type and the alias. Here is an example similar to yours above: alias Num = int; auto foo(T)(T m) { import std.stdio : writeln; static if (is(T : int)) { writeln("here"); } else static if (is(T : Num)) { writeln("there"); } else { static assert(0, "should not be here"); } } void main() { Num x; int y; foo(x); foo(y); } Both of them go to "here". The `Num` one does not go to "there". That is, it can't be distinguished whether we have an int because of an alias or because we actually had an int. And that I understand that it is one of the important points of aliases. Of course, I may have missed / misunderstood something badly in all of that, but I understand that distinguishing them (e.g. in your example above) makes template aliases different than normal aliases.
May 20 2019
On Monday, 20 May 2019 at 17:36:35 UTC, Stefanos Baziotis wrote:[snip] Both of them go to "here". The `Num` one does not go to "there". That is, it can't be distinguished whether we have an int because of an alias or because we actually had an int. And that I understand that it is one of the important points of aliases. Of course, I may have missed / misunderstood something badly in all of that, but I understand that distinguishing them (e.g. in your example above) makes template aliases different than normal aliases.Yes, that's what you would expect to happen with a normal alias. If you change the alias to alias Num(T) = T; and the second is statement to is(T : Num!U, U) then it becomes equivalent to what I had done above. This fails for the same reason as discussed above.
May 20 2019
On Monday, 20 May 2019 at 18:12:08 UTC, jmh530 wrote:If you change the alias to alias Num(T) = T; and the second is statement to is(T : Num!U, U) then it becomes equivalent to what I had done above. This fails for the same reason as discussed above.But with the above reasoning we want this to fail (that is, evaluate is(T == int) as true and go there) because aliases and template aliases are indistinguishable from the type they alias.
May 20 2019
On Monday, 20 May 2019 at 18:20:06 UTC, Stefanos Baziotis wrote:[snip] But with the above reasoning we want this to fail (that is, evaluate is(T == int) as true and go there) because aliases and template aliases are indistinguishable from the type they alias.Sorry, it would be true for Num!int, but false for Num!float or something else.
May 20 2019
On Monday, 20 May 2019 at 19:05:06 UTC, jmh530 wrote:On Monday, 20 May 2019 at 18:20:06 UTC, Stefanos Baziotis wrote:It makes more sense if I said it should evaluate is(T==int) in the first static if for Num!int and then go to the second static if and evaluate is(T==float) for Num!float. That's just because of the way you have the static ifs set up.[snip] But with the above reasoning we want this to fail (that is, evaluate is(T == int) as true and go there) because aliases and template aliases are indistinguishable from the type they alias.Sorry, it would be true for Num!int, but false for Num!float or something else.
May 20 2019
On Monday, 20 May 2019 at 19:07:49 UTC, jmh530 wrote:On Monday, 20 May 2019 at 19:05:06 UTC, jmh530 wrote:If the second static if is `if(is(T == float))` and we passed float or Num!float, yes it should go there. If it is as what we said earlier, `is(T : Num!U, U)` it should not evaluate it to true with the reasoning I said above. That is, it doesn't distinguish an int from a Num!int or a float from a Num!float. It goes back to the assumption that aliases and template aliases are indistinguishable from the type they alias. Again, that is what I _assume_ that is the general direction. The DIP is a step towards that (basically, the root of the problem that the DIP solves is that the compiler sees the alias and the actual type as 2 different types). Also, that is what _I_ would think as the better alternative of the 2. That is not to say that it is the correct / best one. Certainly, it's what the compiler source seems to move towards. During the semantic analysis, the type of a Num!int variable, becomes an int. I surely don't have a lot of experience with the compiler internals, but as far as I know, there's no clear / easy way to know whether it came from an alias or an int.On Monday, 20 May 2019 at 18:20:06 UTC, Stefanos Baziotis wrote:It makes more sense if I said it should evaluate is(T==int) in the first static if for Num!int and then go to the second static if and evaluate is(T==float) for Num!float. That's just because of the way you have the static ifs set up.[snip] But with the above reasoning we want this to fail (that is, evaluate is(T == int) as true and go there) because aliases and template aliases are indistinguishable from the type they alias.Sorry, it would be true for Num!int, but false for Num!float or something else.
May 20 2019
On Monday, 20 May 2019 at 19:25:37 UTC, Stefanos Baziotis wrote:[snip]Sorry, I was getting confused. I was thinking the Num was from a struct, rather than from an alias. Yes, you're right.
May 20 2019
On Monday, 20 May 2019 at 19:44:57 UTC, jmh530 wrote:On Monday, 20 May 2019 at 19:25:37 UTC, Stefanos Baziotis wrote:It's ok, thank you for the feedback![snip]Sorry, I was getting confused. I was thinking the Num was from a struct, rather than from an alias. Yes, you're right.
May 20 2019
On Saturday, 18 May 2019 at 12:32:13 UTC, Stefanos Baziotis wrote: | https://github.com/baziotis/DIPs/blob/master/DIPs/DIP1021.md Assume you mean: https://github.com/baziotis/DIPs/blob/master/Drafts/1NNN-SB.md
May 20 2019
On Monday, 20 May 2019 at 14:27:48 UTC, Nick Treleaven wrote:On Saturday, 18 May 2019 at 12:32:13 UTC, Stefanos Baziotis wrote: | https://github.com/baziotis/DIPs/blob/master/DIPs/DIP1021.md Assume you mean: https://github.com/baziotis/DIPs/blob/master/Drafts/1NNN-SB.mdYes, thank you, I updated it just yesterday and forgot it.
May 20 2019