Bug 6579 – Calling static method should *require* using type and not instance, unless specified by author

Status
RESOLVED
Resolution
INVALID
Severity
enhancement
Priority
P2
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2011-08-30T05:33:42Z
Last change time
2019-12-19T16:26:30Z
Assigned to
No Owner
Creator
Steven Schveighoffer
See also
https://issues.dlang.org/show_bug.cgi?id=14170, https://issues.dlang.org/show_bug.cgi?id=12228

Comments

Comment #0 by schveiguy — 2011-08-30T05:33:42Z
Consider: struct S { static int i; int x; static void reset() {i = i.init;} } S instance; assert(instance.x == 0); instance.x = 5; instance.reset(); assert(instance.x == 0); // fails! This appears to affect instance, but actually it does nothing to the instance, it only affects the static data. I think it should require using the type, via: S.reset(); or typeof(instance).reset(); This makes it clear that the instance plays no role in the function call. This also paves the way for bug 3345 I think also this should apply to member fields. However, enums and aliases should continue to be accessible using the instance.
Comment #1 by schveiguy — 2011-08-30T08:47:13Z
This is in response to: http://d.puremagic.com/issues/show_bug.cgi?id=3345#c3 It's only a poorly chosen name because of the way you can call static functions on instances. What would be a better name, resetStatic()? I mean, why must I repeat that it's static, I've already said it was static! The problem is the coupling between static function names and instances is not an obvious connection. When naming a static function one seldom considers how it will read if called on an instance, because one's expecting people to use the class/struct name to call it. Another example, let's say you have a File struct (not unheard of). Then you want a static method to open a file. A logical thing to do would be: struct File { static File open(string name) {...} } expecting the usage: auto f = File.open("blah.txt"); However, that makes this also valid: File f; f.open("blah.txt"); Which doesn't do even close to what you would expect it to, yet compiles happily. I can't even think of a way to name that function which makes it look like "you're doing the wrong thing" on the instance, yet is good enough to be used as a function name. A solution can be to just put the function outside the struct, calling it openFile. But it almost feels like D is discouraging static methods, use global functions instead. I dispute that this is more annoying for generic code. Calling a static method from an instance is not a usual occurrence, and typically type names in generic code are T or S. How horrible is it to do T.foo() vs. t.foo()? Note that even though this would be a breaking change, it would be a loud one, not a silent one. Any existing code relying on calling static methods from instances would not compile.
Comment #2 by schveiguy — 2011-08-30T09:35:19Z
Revised proposal: One cannot call a static method using an instance method unless the author aliases it into the instance namespace. Andrei pointed out that one can design a static method *intending* it to be used on an instance, for example to save a virtual method call when it's not needed, but still implement a generic interface. For the cases where a static method was intended to be callable from an instance, there should be a provision for that. A sample proposal: struct S { static void foo() {} alias foo this.foo; } Now an instance of S can be used to call foo.
Comment #3 by art.08.09 — 2012-06-02T11:07:52Z
(In reply to comment #2) > Revised proposal: > > One cannot call a static method using an instance method unless the author > aliases it into the instance namespace. > > Andrei pointed out that one can design a static method *intending* it to be > used on an instance, for example to save a virtual method call when it's not > needed, but still implement a generic interface. > > For the cases where a static method was intended to be callable from an > instance, there should be a provision for that. > > A sample proposal: > > struct S > { > static void foo() {} > alias foo this.foo; > } > > Now an instance of S can be used to call foo. a) I don't see any real need to separate type and instance methods b) OTOH i don't think it can do much harm either. But overloading alias further does not seem like the best idea. 'static @method void foo() {}' - @method is needed for UFCS too, where it would do something similar.
Comment #4 by gor — 2012-06-03T05:28:22Z
(In reply to comment #3) > (In reply to comment #2) > > Revised proposal: > > > > One cannot call a static method using an instance method unless the author > > aliases it into the instance namespace. > > > > Andrei pointed out that one can design a static method *intending* it to be > > used on an instance, for example to save a virtual method call when it's not > > needed, but still implement a generic interface. > > > > For the cases where a static method was intended to be callable from an > > instance, there should be a provision for that. > > > > A sample proposal: > > > > struct S > > { > > static void foo() {} > > alias foo this.foo; > > } > > > > Now an instance of S can be used to call foo. > > a) I don't see any real need to separate type and instance methods > b) OTOH i don't think it can do much harm either. But overloading alias further > does not seem like the best idea. 'static @method void foo() {}' - @method is > needed for UFCS too, where it would do something similar. It hinders overloading. For so long I wanted to be able to have both static and non-static opDispatch and opCall and couldn't because of this. This behavior is not doing any good anyway.
Comment #5 by art.08.09 — 2012-06-03T06:42:55Z
(In reply to comment #4) > It hinders overloading. For so long I wanted to be able to have both static and > non-static opDispatch and opCall and couldn't because of this. This is a very good argument. opCall&co are a bit special, but doing that should be possible w/o special-casing them. > This behavior is not doing any good anyway. It has worked like that for a long time, and it is useful sometimes. But the old behavior can be easily emulated with something like static int f() {}; auto f(A...)(A a) { return typeof(this).f(a); }
Comment #6 by timon.gehr — 2012-06-03T06:51:42Z
(In reply to comment #4) > It hinders overloading. For so long I wanted to be able to have both static and > non-static opDispatch and opCall and couldn't because of this. This behavior is > not doing any good anyway. This issue is not closely related to overloading. Slightly refining the overloading rules as proposed in issue 3345 would already solve the problem. struct S{ static void foo(){...} void foo(){...} } struct T{ static void foo(){...} void bar(){...} } void main(){ S s; T t; S.foo(); // calls the static version s.foo(); // calls the instance version T.foo(); // ok t.foo(); // ok // T.bar() // error t.bar(); // ok }
Comment #7 by art.08.09 — 2012-06-03T07:54:14Z
(In reply to comment #6) > Slightly refining the overloading rules as proposed in issue 3345 would already > solve the problem. > struct T{ > static void foo(){...} > void bar(){...} > } > t.foo(); // ok This seems too dangerous to allow (by default). Do static and non-static methods form one overload set? struct S { static auto opCall(int) {...} auto opCall(string) {...} } Oh, and i just noticed the suggestion in this report that static fields should not be accessible via an instance - no, that would a break a lot of code, for almost no gain (you can already place the static data in another struct and embed that one in an (anonymous) union)
Comment #8 by schveiguy — 2012-06-04T10:01:16Z
(In reply to comment #7) > Oh, and i just noticed the suggestion in this report that static fields should > not be accessible via an instance - no, that would a break a lot of code, for > almost no gain (you can already place the static data in another struct and > embed > that one in an (anonymous) union) That is not as important as the method calls. At least with instance access to static data, you are addressing the data named by the call. So for my original example: instance.i = 5; This is correctly addressing a variable named i, even though it's not part of the instance. However, with: instance.reset(); This implies you are passing instance as a parameter when you are actually not.
Comment #9 by dfj1esp02 — 2018-05-16T12:30:55Z
Dunno, this would complicate the language and lower its usability without a clear problem and gain. How would code then decide whether to call instance.method() or typeof(instance).method()?
Comment #10 by schveiguy — 2018-05-16T14:55:01Z
This is a pretty old issue, and I'm not sure it's super-important. But to answer your question, the idea would be for the type to opt-in on having a static function be considered part of the instance by aliasing. If it didn't do that, then the function wouldn't be part of the instance, so you would have to use typeof. It's a choice the type designer should make. This does solve a problem for functions that are static and named in a way that confuses what they do. It opens up a way to have functions that use the type to clarify what the function is doing. The example I brought up earlier, imagine you have a Stream type, and you have a static function "open(char [] name)" that creates a new stream. This can cause confusion: Stream s; s.open("foo.txt"); // creates a new stream and returns it, then it goes out of scope immediately assert(s.isOpen); // fails! However, when used properly, it reads correctly: auto s = Stream.open("foo.txt"); assert(s.isOpen); I can rename the function something else, e.g. "createNewFile" or something, which doesn't look correct on an instance, but if the language can require using the type name, then it's easier to design. Basically, it gives more power to the type designer on how his API looks. Given that there are numerous workarounds, including factory functions outside the type, and UFCS, I'm not sure how much this enhancement is still relevant. It just means anyone adding static functions to a type has to consider how it looks being run on an instance, and likely choose something outside the type instead.
Comment #11 by razvan.nitu1305 — 2019-12-19T16:26:30Z
I think that too much time has passed and it's too late to change this since code out there is already using this so called feature. I'm going to close this as INVALID.