digitalmars.D.learn - class template conflict
- Michelle Long (30/30) Dec 23 2018 class X
- Basile B. (15/46) Dec 23 2018 You have this option too:
- Michelle Long (1/2) Dec 23 2018 The whole point is to use the same identifier ;/
- bauss (19/21) Dec 25 2018 I think there is a bigger problem at stake here in terms of
- Neia Neutuladh (53/59) Dec 25 2018 A probably abstract base class with only one child class. Normally you
- Michelle Long (2/9) Dec 25 2018 No, the problem is reasoning from the conclusion...
- Steven Schveighoffer (12/26) Dec 23 2018 I think it has less to do with class names and more to do with symbol
- Daniel Kozak (10/17) Dec 24 2018 ne 23. 12. 2018 13:10 odes=C3=ADlatel Michelle Long via Digitalmars-d-le...
- Michelle Long (35/55) Dec 25 2018 But I am not talking about inside the template being used. The
- Neia Neutuladh (27/31) Dec 25 2018 The following are entirely equivalent:
- Neia Neutuladh (16/16) Dec 25 2018 On Tue, 25 Dec 2018 16:55:36 +0000, Neia Neutuladh wrote:
class X { } class X(int N) : X { } Is there any real reason we can't do this? It is very nice to be able to treat X like the base and X!n as a derived class. Sure we can do class X(int N) : X!0 { static if(N == 0) { } } but this is very ugly, in my code I always have to use X!0 as the basis! I do not think there is any harm to allow this since the templated class always has to specify N. It is not like we can do class X(int N = 0) : X { static if(N == 0) { } } Actually we can, so... I don't see the point in not allowing the first case, they are logically equivalent. That static if is just ugly and it is defining the base class inside the derived class which seems unnatural.
Dec 23 2018
On Sunday, 23 December 2018 at 12:09:31 UTC, Michelle Long wrote:class X { } class X(int N) : X { } Is there any real reason we can't do this? It is very nice to be able to treat X like the base and X!n as a derived class. Sure we can do class X(int N) : X!0 { static if(N == 0) { } } but this is very ugly, in my code I always have to use X!0 as the basis! I do not think there is any harm to allow this since the templated class always has to specify N. It is not like we can do class X(int N = 0) : X { static if(N == 0) { } } Actually we can, so... I don't see the point in not allowing the first case, they are logically equivalent. That static if is just ugly and it is defining the base class inside the derived class which seems unnatural.You have this option too: ``` template X(N...) if (N.length == 0 || N.length == 1 && is(typeof(N[0]) == int)) { static if (N.length == 0) class X {} else class X : X!() {} } auto base = new X!(); auto derived = new X!8; ``` More simple is : do not use the same identifier ;)
Dec 23 2018
More simple is : do not use the same identifier ;)The whole point is to use the same identifier ;/
Dec 23 2018
On Monday, 24 December 2018 at 00:24:05 UTC, Michelle Long wrote:I think there is a bigger problem at stake here in terms of software architecture. What's the point needed for them to have the same identifier? Clearly the two classes will have two different functions and should be named accordingly. Ex. class X { ... } class X(int N) { ... } Could be something like: class X { ... } class XWithArguments { ... } There is absolutely no point of them having same identifiers unless they did the exact same thing and in that case you'd probably just not want this anyway. A solution to this would be something like: interface X { } class XWithoutArguments : X { } class XWithArguments : X { }More simple is : do not use the same identifier ;)The whole point is to use the same identifier ;/
Dec 25 2018
On Tue, 25 Dec 2018 18:34:04 +0000, bauss wrote:I think there is a bigger problem at stake here in terms of software architecture. What's the point needed for them to have the same identifier?A probably abstract base class with only one child class. Normally you have "Foo" and "FooImpl", or "IFoo" and "Foo", but that's ugly. This was the primary example that Michelle Long gave. I'd expect that a class Foo {} class Foo<T> : Foo {} class Foo<T1, T2> : Foo {} happen to have type parameters. In D, you'd have one class and two templates, and you can't overload two symbols of different kinds, so you have to write it as: class Foo() {} class Foo(T): Foo!() {} class Foo(T, U): Foo!() {} In Java, for legacy reasons, this pattern is baked into the language; Foo<T> always has another class, Foo, that it implicitly casts to and from. Some people take advantage of that. Most people who do both metaprogramming and OOP in D and have been doing that for a nontrivial amount of time probably encountered this exact same thing in D. I encountered it the first time before D2 was a thing.Clearly the two classes will have two different functions and should be named accordingly.They will have different implementations and may have different interfaces. The proposed use case is inheritance, so X!10's public interface will be a superset of X's (and likely identical). To give a slightly less contrived example, let's say you want to have some functions available to a scripting language. (I'm doing something like this in a side project for a CLI spreadsheet program.) Each function has a display name, help text, argument validation, and the function body. If I had done this in an OOP style (and I might end up reworking it to look more like this), I'd have something like: interface Function { string helpText(); string name(); Nullable!Error validateParameters(Val[] parameters); Val execute(Val[] parameters); } And then I'd have a convenience mechanism to produce a conforming class from a function with UDAs: class FunctionImpl(alias fn) : Function { override: string helpText() { return getUDAs!(fn, Help)[0].text; } string name() { return __traits(identifier, fn); } // etc } It would be slightly nicer to just have "Function" everywhere instead of both Function and FunctionImpl. Not enough to justify the complexity of the symbol lookup rules required. Not enough to make `class Foo(T)` mean something different from `template Foo(T) class Foo`. But it *would* be slightly nicer, and it would make things slightly more straightforward for
Dec 25 2018
On Tuesday, 25 December 2018 at 18:34:04 UTC, bauss wrote:On Monday, 24 December 2018 at 00:24:05 UTC, Michelle Long wrote:No, the problem is reasoning from the conclusion...I think there is a bigger problem at stake here in terms of software architecture.More simple is : do not use the same identifier ;)The whole point is to use the same identifier ;/
Dec 25 2018
On 12/23/18 7:09 AM, Michelle Long wrote:class X { } class X(int N) : X { } Is there any real reason we can't do this?I think it has less to do with class names and more to do with symbol overloading. The only place I think templates are allowed to overload names with non-templates is functions, which actually was not always the case (you used to have to only have templates or non templates as function overloads).It is very nice to be able to treat X like the base and X!n as a derived class.The problem I see is: template foo(alias A) { ... } foo!X Did you mean class X or template X? For functions, this is OK, because it's one overload set. -Steve
Dec 23 2018
ne 23. 12. 2018 13:10 odes=C3=ADlatel Michelle Long via Digitalmars-d-learn= < digitalmars-d-learn puremagic.com> napsal:class X { } class X(int N) : X { } Is there any real reason we can't do this?Actually yes. It would break almost all of my code. In D you can do thing like this: class X(int N) { X something; // it is same as X!N something; } So I do not need to write X!N everywhere inside X class template
Dec 24 2018
On Monday, 24 December 2018 at 22:55:55 UTC, Daniel Kozak wrote:ne 23. 12. 2018 13:10 odesÃlatel Michelle Long via Digitalmars-d-learn < digitalmars-d-learn puremagic.com> napsal:How would it break your code?class X { } class X(int N) : X { } Is there any real reason we can't do this?Actually yes. It would break almost all of my code.In D you can do thing like this: class X(int N) { X something; // it is same as X!N something; } So I do not need to write X!N everywhere inside X class templateBut I am not talking about inside the template being used. The whole point of doing this is so that one can refer to the base class using the same name as the derived with a template parameter to make a composite structure. X!N should be totally different than X. The fact that you can use X inside a class to refer to X!N is a hack... and in any case should not effect what I'm talking about because it is only used in the inherited part.. which it would be circular to use it as it is: class X(int N) : X { } creates circular inheritance. so for the inherited part it should never be used and you never use it in your code like that... so it won't break anything. Also, as long as there is no other symbol with that name it won't break anything. Suppose this did work: class X; class X(int N) : X // (Here X refers to the base class above { // Using X can still be X!N since we can just alias the original X away. } class X; alias XX = X; class X(int N) : X { X x; // X!N x; XX xx; // X = x; } So it would not break anything. It really has nothing to do with what one does inside a template but how it looks to the outside.
Dec 25 2018
On Tue, 25 Dec 2018 13:03:13 +0000, Michelle Long wrote:But I am not talking about inside the template being used. The whole point of doing this is so that one can refer to the base class using the same name as the derived with a template parameter to make a composite structure.The following are entirely equivalent: class X(int N) : X {} template X(int N) { class X : X {} } You want to be able to do, essentially: class X {} template X(int N) { // `: X` somehow refers to the X in the outer scope class X : X {} } And sure, this specific case obviously doesn't work if the `: X` refers to the template or the class inside the template. But the symbol lookup rules aren't "try everything and see what sticks". You look up the nearest symbol and then you just use that. It would be like arguing that, in the following example, the compiler should know that ints aren't callable and should call the function: void foo() { writeln("merr critmis"); } void main() { int foo = 10; foo(); } Which isn't obviously wrong, but it does make things harder to understand.
Dec 25 2018
On Tue, 25 Dec 2018 16:55:36 +0000, Neia Neutuladh wrote: And I forgot part of it. Let's say we did the work to make this function: class X {} template X(int N) { // `: X` somehow refers to the X in the outer scope class X : X {} } How do you distinguish between the base class and the derived class in there? You'd have to use typeof(this) and typeof(super) everywhere. And externally, how do you refer to class X and template X separately? If you have a template with an alias parameter and pass X, how do you pass class-X and how do you pass template-X? This is already unpleasant with functions, and there's a way to distinguish them.
Dec 25 2018