[Issue 8381] Uniform function call syntax (pseudo member) enhancement suggestions
https://issues.dlang.org/show_bug.cgi?id=8381 Vladimir Panteleev changed: What|Removed |Added Status|NEW |RESOLVED Resolution|--- |INVALID --- Comment #8 from Vladimir Panteleev --- (In reply to Tommi from comment #0) > So, I'm suggesting these two features to be added to the language: Hello, Changes to the language need to be proposed through the DIP process: https://github.com/dlang/DIPs Bugzilla is not the correct place for language enhancements. If you still believe your proposal has merit, please submit a DIP. The current DIP manager can assist you through the process. --
[Issue 8381] Uniform function call syntax (pseudo member) enhancement suggestions
https://issues.dlang.org/show_bug.cgi?id=8381 Andrei Alexandrescu changed: What|Removed |Added Version|unspecified |D2 --
[Issue 8381] Uniform function call syntax (pseudo member) enhancement suggestions
http://d.puremagic.com/issues/show_bug.cgi?id=8381 --- Comment #7 from David Piepgrass 2012-07-12 12:21:21 PDT --- > It almost looks like we're trying to pass MyType as first argument to the > function, but because we can't pass type as function argument, we pass it as > first template argument. But you're right that none of that is very obvious. > Maybe the whole uniform function call syntax could have been implemented > somehow differently so that it had been more intuitive to people new to the > language. Well, yeah, maybe. I could probably think of some alternatives that are a bit more intuitive than UFCS. For example I think it would be nice to allow "alternate" interfaces to types, something like: alias MyInt : int { bool isPrime() const { ... } } MyInt x = 7; writeln(x.isPrime()); // OK int y = x+1; writeln(y.isPrime()); // ERROR, no such function in "int" >But I really mean type when I say, "extending the functionality of a type". I >mean not only class but all user defined types as well, like enum, struct and >union. My proposal could easily include enums and structs, too: static enum Goo { static Goo g() {...} } static struct Hoo { static void f() {...} } It reminds me that C# allows "new Hoo()" for structs even though they are not allocated on the heap. If that existed in D then the "static new" part of my proposal could work well for structs, too. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email --- You are receiving this mail because: ---
[Issue 8381] Uniform function call syntax (pseudo member) enhancement suggestions
http://d.puremagic.com/issues/show_bug.cgi?id=8381 --- Comment #6 from David Piepgrass 2012-07-12 12:06:36 PDT --- Argh, so many typos, I should be careful when I rename things... > // Static helper method provides an easy way to create MyClass > public static Foo LoadFrom(string filename, ...) > However, the client didn't like that, and insisted that Create() should be a > constructor "for consistency". I was able to rearrange things to make Create() > into a constructor, but my code was a little clunkier that way. s/MyClass/Foo/ s/Create/LoadFrom/ Oh how nice it would be if we could simply correct our posts. > You could also argue that it's not obvious that: > var.func(arg); > > ...can "transform" itself into: > func(var, arg); That feature is used very often, so newcomers will learn it quickly. And it is definitely a more obvious transformation than func!Type(...). P.S. I'm just throwing it out there, but couldn't classes themselves be treated as objects, a la Objective C? That could open the door to another approach, in which UFCS applies to class objects. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email --- You are receiving this mail because: ---
[Issue 8381] Uniform function call syntax (pseudo member) enhancement suggestions
http://d.puremagic.com/issues/show_bug.cgi?id=8381 --- Comment #5 from Tommi 2012-07-12 10:50:58 PDT --- One small point I forgot to mention about my lowering proposal, which is that besides adding static pseudo-member functions, you can also add types. For example: struct WrapInt { int m_value; } template ValueType(T) if (is(T == WrapInt)) { alias int ValueType; } void main(string[] args) { WrapInt.ValueType value = 12; // gets lowered into: // ValueType!WrapInt value = 12; } -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email --- You are receiving this mail because: ---
[Issue 8381] Uniform function call syntax (pseudo member) enhancement suggestions
http://d.puremagic.com/issues/show_bug.cgi?id=8381 deadalnix changed: What|Removed |Added CC||deadal...@gmail.com --- Comment #4 from deadalnix 2012-07-12 10:23:56 PDT --- (In reply to comment #3) > (In reply to comment #1) > > - The rule is non-obvious. How could a new developer possibly guess that > > this > > happens? > > You could also argue that it's not obvious that: > var.func(arg); > > ...can "transform" itself into: > func(var, arg); > > I don't think it's that much different to transform: > MyType.func(arg); > > ...into: > func!(MyType)(arg); > This is the most obvious way to transform that. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email --- You are receiving this mail because: ---
[Issue 8381] Uniform function call syntax (pseudo member) enhancement suggestions
http://d.puremagic.com/issues/show_bug.cgi?id=8381 --- Comment #3 from Tommi 2012-07-12 10:08:16 PDT --- (In reply to comment #1) > - The rule is non-obvious. How could a new developer possibly guess that this > happens? You could also argue that it's not obvious that: var.func(arg); ...can "transform" itself into: func(var, arg); I don't think it's that much different to transform: MyType.func(arg); ...into: func!(MyType)(arg); It almost looks like we're trying to pass MyType as first argument to the function, but because we can't pass type as function argument, we pass it as first template argument. But you're right that none of that is very obvious. Maybe the whole uniform function call syntax could have been implemented somehow differently so that it had been more intuitive to people new to the language. (In reply to comment #1) > Supposing that I would like to extend the set of static members in class Foo. > How about: > > module A; > // assuming "static class" does not already have any meaning? > static class Foo { > static void F(); > static int x; > } But I really mean type when I say, "extending the functionality of a type". I mean not only class but all user defined types as well, like enum, struct and union. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email --- You are receiving this mail because: ---
[Issue 8381] Uniform function call syntax (pseudo member) enhancement suggestions
http://d.puremagic.com/issues/show_bug.cgi?id=8381 --- Comment #2 from David Piepgrass 2012-07-12 09:24:36 PDT --- > (not merely a @disabled default constructor, but no constructor at all). Also, declaring an instance of a static class is not possible either, so that given > module Q; > static class Foo { >static P.Foo new(Bar b, Baz z) { >... >return new P.Foo(...); >} >} The meaning is unambiguous in: Foo f = new Foo(Bar(...), Baz(...)); // equivalent to P.Foo f = Q.Foo.new(Bar(...), Baz(...)); -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email --- You are receiving this mail because: ---
[Issue 8381] Uniform function call syntax (pseudo member) enhancement suggestions
http://d.puremagic.com/issues/show_bug.cgi?id=8381 David Piepgrass changed: What|Removed |Added CC||qwertie...@gmail.com --- Comment #1 from David Piepgrass 2012-07-12 09:11:28 PDT --- To some extent I like the goal of the proposal, but I don't like the implementation: - The rule is non-obvious. How could a new developer possibly guess that this happens? - It would get lowered to a call to a template function, but usually the programmer wants to extend one specific type. So I offer the following counterproposal: 1) Adding static member functions Supposing that I would like to extend the set of static members in class Foo. How about: module A; // assuming "static class" does not already have any meaning? static class Foo { static void F(); static int x; } This block defines members in a class namespace "Foo". If "regular class Foo" is defined in module A then these static members go into the same class. However, if the original Foo is defined in module B then "static class Foo" goes into module A and is considered a separate class. Now consider some code that tries to use F(): module C; import A; import B; void code() { Foo.F(); } Currently, the compiler complains that B.Foo and A.Foo conflict with each other. I propose changing this rule when accessing static members. The compiler does not need to declare a conflict as soon as it sees "Foo", instead it can look for static members in ALL Foo classes, using the same anti-hijacking rules that it uses for free-standing module functions. The purpose of "static class" is not to facilitate this method lookup per se; if A and B both contain non-static Foo classes, the compiler should still search both classes for static members rather than giving an error immediately. Rather, the main purpose of "static class" is to declare that the class has no constructor (not merely a @disabled default constructor, but no constructor at all). Therefore, "new Foo()" cannot mean "new A.Foo", leading the compiler to the interpretation "new B.Foo". Also, multiple static classes can be defined with the same name in the same module, and their contents are merged. IMO, when searching for static members, a class scope should pretty much behave the same way as module scope. So the compiler should not report an ambiguous call if the call has only one interpretation. In the above case, the compiler should report an error if and only if a B.F() function exists. One more thing, if module C declares a "class Foo" then it takes priority over A.Foo and B.Foo. But if C declares "static class Foo" then C.Foo should allow the same overloading behavior just described; thus "new Foo()" should still mean "new B.Foo()" and "Foo.F()" should still mean "A.Foo.F()". I think an equivalent way to say this is that, inside C, the lookup rules proceed as if C.Foo were declared outside module C and imported into C. 2) Adding constructors aka "Constructors Considered Harmful" Ahh, constructors, constructors. In my opinion the constructor design in most languages including C++, C#, D and Java is flawed, because it exposes an implementation detail that should not be exposed, namely, which class gets allocated and when. I offer you as "exhibit L" the Lazy class in the .NET framework. This class's main purpose is to compute a value the first time you access it. For example: int x; Lazy lazy = new Lazy(() => 7 * x); x = 3; x = lazy.Value; // 21 x = lazy.Value; // still 21 (Value is initialized only once) There is an annoying issue though Lazy operates in some different modes, and it also contains a member and extra code to aid debugging. So in addition to holding the value itself and a reference to the initializer delegate, it's got a couple of other member variables and the Value property has to check a couple of things before it returns the value. So what if, in the future, MS decided to optimize Lazy for its default mode of operation, and factor out other modes into derived class(es)? Well, they can't do that. If MS wants to return a LazyThreadSafe object (derived from Lazy) when the user requests the thread-safe mode, they can't do that because all the clients are saying "new Lazy", which can only return the exact class Lazy and nothing else. MS could add a static function, Lazy.New(...) but it's too late now that the interface is already defined with all those public constructors. As "exhibit 2", a.k.a. "exhibit Foo", I recently wrote a library where I needed to provide a constructor that does a bunch of initialization work (that may fail) before it actually creates the object. By far the most natural implementation was a static member function: // Constructor with dependency injection public Foo(arg1, arg2, arg3) { } // Static helper method provides an easy way to create MyClass public static Foo LoadFrom(string file