www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Uniform call syntax for implicit this.

reply %u <dflgkd sgjds.com> writes:
When implemented, will uniform call syntax work for the "this"
object even if not specified?

For example, will foo() get called in the following example?

void foo(A a, int b) {}

class A {
    void test() {
        this.foo(10);
        foo(10);
    }
}


Thanks
Feb 02 2011
next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Wednesday 02 February 2011 20:48:15 %u wrote:
 When implemented, will uniform call syntax work for the "this"
 object even if not specified?
 
 For example, will foo() get called in the following example?
 
 void foo(A a, int b) {}
 
 class A {
     void test() {
         this.foo(10);
         foo(10);
     }
 }

I would think so, since I don't think that the this pointer/reference is treated in any special way. The only special thing about it as far as I know is that it's filled in for you in cases where you use member variables or call member functions without it. However, as UFCS has yet to be implemented, I'm not sure that we can be quite sure of how it's going to work. There are going to be a number of related features - such as alias this, opDispatch, and cases where there's a member function and a free function with the same name - which are going to have to be able to interact unambiguously, and I don't think that all of that has been entirely sorted out yet. - Jonathan M Davis
Feb 02 2011
prev sibling next sibling parent spir <denis.spir gmail.com> writes:
On 02/03/2011 05:48 AM, %u wrote:
 When implemented, will uniform call syntax work for the "this"
 object even if not specified?

 For example, will foo() get called in the following example?

 void foo(A a, int b) {}

 class A {
      void test() {
          this.foo(10);
          foo(10);
      }
 }


 Thanks

Do you mean in the last line? I hope not! Adding implicit reformulation on implicit reformulation does not help & making code readable. foo(10) --> this.foo(10) --> foo(this, 10) Anyway, a sensible limit can be placed in that foo is not a function member of A, (hopefully) preventing the first rewriting step to occur. Denis -- _________________ vita es estrany spir.wikidot.com
Feb 03 2011
prev sibling next sibling parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2011-02-02 23:48:15 -0500, %u <dflgkd sgjds.com> said:

 When implemented, will uniform call syntax work for the "this"
 object even if not specified?
 
 For example, will foo() get called in the following example?
 
 void foo(A a, int b) {}
 
 class A {
     void test() {
         this.foo(10);
         foo(10);
     }
 }

I think it should work. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Feb 03 2011
next sibling parent reply Daniel Gibson <metalcaedes gmail.com> writes:
Am 03.02.2011 15:57, schrieb Michel Fortin:
 On 2011-02-02 23:48:15 -0500, %u <dflgkd sgjds.com> said:
 
 When implemented, will uniform call syntax work for the "this"
 object even if not specified?

 For example, will foo() get called in the following example?

 void foo(A a, int b) {}

 class A {
     void test() {
         this.foo(10);
         foo(10);
     }
 }

I think it should work.

I think foo(10) should *not* be equivalent to foo(this, 10).
Feb 03 2011
next sibling parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2011-02-03 12:43:12 -0500, Daniel Gibson <metalcaedes gmail.com> said:

 Am 03.02.2011 15:57, schrieb Michel Fortin:
 On 2011-02-02 23:48:15 -0500, %u <dflgkd sgjds.com> said:
 
 When implemented, will uniform call syntax work for the "this"
 object even if not specified?
 
 For example, will foo() get called in the following example?
 
 void foo(A a, int b) {}
 
 class A {
 void test() {
 this.foo(10);
 foo(10);
 }
 }

I think it should work.

I think foo(10) should *not* be equivalent to foo(this, 10).

Personally, I'm not sure whether the uniform call syntax will be this much useful or not, but if it gets implemented I think foo(10) should be equivalent to foo(this, 10) in the case above. That said, it should not be ambiguous: if there is a member function foo and a global function foo and both matches the call, it's ambiguous and it should be an error. Can this work in practice? We probably won't know until we have an implementation to play with. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Feb 03 2011
parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2011-02-03 13:42:30 -0500, Jonathan M Davis <jmdavisProg gmx.com> said:

 On Thursday, February 03, 2011 09:54:44 Michel Fortin wrote:
 On 2011-02-03 12:43:12 -0500, Daniel Gibson <metalcaedes gmail.com> said:
 Am 03.02.2011 15:57, schrieb Michel Fortin:
 On 2011-02-02 23:48:15 -0500, %u <dflgkd sgjds.com> said:
 When implemented, will uniform call syntax work for the "this"
 object even if not specified?
 
 For example, will foo() get called in the following example?
 
 void foo(A a, int b) {}
 
 class A {
 void test() {
 this.foo(10);
 foo(10);
 }
 }

I think it should work.

I think foo(10) should *not* be equivalent to foo(this, 10).

Personally, I'm not sure whether the uniform call syntax will be this much useful or not, but if it gets implemented I think foo(10) should be equivalent to foo(this, 10) in the case above. That said, it should not be ambiguous: if there is a member function foo and a global function foo and both matches the call, it's ambiguous and it should be an error. Can this work in practice? We probably won't know until we have an implementation to play with.

Except that if you have both a member function foo and a free function foo, how can you tell the compiler which to use?

Indeed, that's a problem. The solution is to sidestep the problem. :-) We just have to disallow declaring a module-level function and a class-level function with the same name and the same parameter types (including 'this' in the parameters). void foo(A a, int i); class A { void foo(int i); // error, same as foo(A, int) } If the module-level function is in a different module, then you can use the module name to disambiguate if necessary. If they're in the same module, the compiler catches the error during semantic analysis of the module. I'm not too sure how disruptive this change would be to existing code. I'm under the impression that this situation is rare, but I can't say for sure. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Feb 03 2011
next sibling parent bearophile <bearophileHUGS lycos.com> writes:
Michel Fortin:

 We just have to disallow declaring a module-level function and a 
 class-level function with the same name and the same parameter types 
 (including 'this' in the parameters).

No more special cases in D, please. Bye, bearophile
Feb 03 2011
prev sibling next sibling parent Michel Fortin <michel.fortin michelf.com> writes:
On 2011-02-03 15:50:17 -0500, spir <denis.spir gmail.com> said:

 How can you propose this, Michel? Complexify the language, and any 
 implementation, just for a non-feature that makes code very hard to 
 decode, by requiring a double mental rewriting operation:
      foo(i) --> this.foo(i) --> foo(this,i)
 what advantage does this feature provide? None. What additional 
 drawback: see thread above.

I'm not really advocating for it. I'm just trying to figure out how it could be done, what impact it'd have, and what limitations should be put on it. This uniform call syntax has been talked a lot but never explored much. I think it's time we look at it so we understand the problems and the solutions and finally decide if its worth it or not. What advantages should it provide? Mainly it makes member functions and standalone functions identical in usage, so whether something is a member or not becomes a implementation detail the user does not have to care about. Obviously, if we find out that it can't be done without complicating things, the feature should be abandoned. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Feb 03 2011
prev sibling parent Michel Fortin <michel.fortin michelf.com> writes:
On 2011-02-03 14:47:26 -0500, Jonathan M Davis <jmdavisProg gmx.com> said:

 I take it then that you're suggesting that the compiler automatically 
 select the
 member function when the conflicting free function is a separate module?

No, I haven't thought about that yet. That's one possibility, but it makes silent hijacking possible. Another is that the call is simply made ambiguous. If it's ambiguous, then you have to prefix it with the correct module or class name. That said, before we go further, I think the complications (there are more) are far worse than the problem uniform call syntax is trying to solve (what problem exactly?). If D was designed with uniform call syntax in mind from the start, then it could probably have worked, but at the point we are now it's probably not that wise to change everything for a feature of unknown benefit. Perhaps we should aim at something simpler... -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Feb 03 2011
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Thursday, February 03, 2011 09:54:44 Michel Fortin wrote:
 On 2011-02-03 12:43:12 -0500, Daniel Gibson <metalcaedes gmail.com> said:
 Am 03.02.2011 15:57, schrieb Michel Fortin:
 On 2011-02-02 23:48:15 -0500, %u <dflgkd sgjds.com> said:
 When implemented, will uniform call syntax work for the "this"
 object even if not specified?
 
 For example, will foo() get called in the following example?
 
 void foo(A a, int b) {}
 
 class A {
 void test() {
 this.foo(10);
 foo(10);
 }
 }

I think it should work.

I think foo(10) should *not* be equivalent to foo(this, 10).

Personally, I'm not sure whether the uniform call syntax will be this much useful or not, but if it gets implemented I think foo(10) should be equivalent to foo(this, 10) in the case above. That said, it should not be ambiguous: if there is a member function foo and a global function foo and both matches the call, it's ambiguous and it should be an error. Can this work in practice? We probably won't know until we have an implementation to play with.

Except that if you have both a member function foo and a free function foo, how can you tell the compiler which to use? Assuming that foo(10) unambigously calls the free function, then you have a way to call the free function - or if .foo(10) calls the free function, then you have a way to call the free function. But how would you call the member function? this.foo(10) is ambiguous, and what other way is there? It seems to me that we'll be forced to make it so that if there's any ambiguity between a free function and a member function, then it's the member function that gets called rather than making the programmer be more explicit like we usually would - simply because there is no way for the programmer to be more explicit. - Jonathan M Davis
Feb 03 2011
prev sibling parent "Robert Jacques" <sandford jhu.edu> writes:
On Thu, 03 Feb 2011 13:42:30 -0500, Jonathan M Davis <jmdavisProg gmx.com>  
wrote:

 On Thursday, February 03, 2011 09:54:44 Michel Fortin wrote:
 On 2011-02-03 12:43:12 -0500, Daniel Gibson <metalcaedes gmail.com>  
 said:
 Am 03.02.2011 15:57, schrieb Michel Fortin:
 On 2011-02-02 23:48:15 -0500, %u <dflgkd sgjds.com> said:
 When implemented, will uniform call syntax work for the "this"
 object even if not specified?

 For example, will foo() get called in the following example?

 void foo(A a, int b) {}

 class A {
 void test() {
 this.foo(10);
 foo(10);
 }
 }

I think it should work.

I think foo(10) should *not* be equivalent to foo(this, 10).

Personally, I'm not sure whether the uniform call syntax will be this much useful or not, but if it gets implemented I think foo(10) should be equivalent to foo(this, 10) in the case above. That said, it should not be ambiguous: if there is a member function foo and a global function foo and both matches the call, it's ambiguous and it should be an error. Can this work in practice? We probably won't know until we have an implementation to play with.

Except that if you have both a member function foo and a free function foo, how can you tell the compiler which to use?

*sigh* The same way you do it today, but using the outer-scope operator '.'. So foo(10) would call the member function, while .foo(10) would call the outer function, which would be re-written as .foo(this,10). An example of this today in D: import std.conv; class Foo { string text; string toString() {return .text("My text is: "text);} }
Feb 03 2011
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 03 Feb 2011 14:15:11 -0500, Michel Fortin  
<michel.fortin michelf.com> wrote:

 On 2011-02-03 13:42:30 -0500, Jonathan M Davis <jmdavisProg gmx.com>  
 said:

 On Thursday, February 03, 2011 09:54:44 Michel Fortin wrote:
 On 2011-02-03 12:43:12 -0500, Daniel Gibson <metalcaedes gmail.com>  
 said:
 Am 03.02.2011 15:57, schrieb Michel Fortin:
 On 2011-02-02 23:48:15 -0500, %u <dflgkd sgjds.com> said:
 When implemented, will uniform call syntax work for the "this"
 object even if not specified?
  For example, will foo() get called in the following example?
  void foo(A a, int b) {}
  class A {
 void test() {
 this.foo(10);
 foo(10);
 }
 }



much useful or not, but if it gets implemented I think foo(10) should be equivalent to foo(this, 10) in the case above. That said, it should not be ambiguous: if there is a member function foo and a global function foo and both matches the call, it's ambiguous and it should be an error. Can this work in practice? We probably won't know until we have an implementation to play with.

foo, how can you tell the compiler which to use?

Indeed, that's a problem. The solution is to sidestep the problem. :-) We just have to disallow declaring a module-level function and a class-level function with the same name and the same parameter types (including 'this' in the parameters). void foo(A a, int i); class A { void foo(int i); // error, same as foo(A, int) } If the module-level function is in a different module, then you can use the module name to disambiguate if necessary. If they're in the same module, the compiler catches the error during semantic analysis of the module. I'm not too sure how disruptive this change would be to existing code. I'm under the impression that this situation is rare, but I can't say for sure.

The more I think about it (and read discussions like this), the more I think that uniform call syntax should only be allowed for types which cannot declare member functions (arrays, primitives, enums, etc.). Otherwise, we introduce new ambiguities that serve little purpose except annoyance. If I have a function foo(A) and A has a member foo(), then foo(myA) should call the free function, myA.foo() should call the member. If this doesn't continue to be correct, we have taken a step backwards. -Steve
Feb 03 2011
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 03 Feb 2011 16:05:01 -0500, spir <denis.spir gmail.com> wrote:

 On 02/03/2011 08:43 PM, Steven Schveighoffer wrote:
 The more I think about it (and read discussions like this), the more I  
 think
 that uniform call syntax should only be allowed for types which cannot  
 declare
 member functions (arrays, primitives, enums, etc.).  Otherwise, we  
 introduce
 new ambiguities that serve little purpose except annoyance.

More precisely, it should work with all types in position of first parameter, but using implicit this like with member functions. This is ok: void f (C c, I i) {...} auto c = new C(); I i; c.f(i); Don't you think so?

But what if C also defines f? I don't think c.f(i) should fail in that case. If it doesn't fail, then surprises/hijacking will occur (hey, I thought I was calling my f, what gives?!). You could write code like this: import std.somemodule : A; void f(A a, int i) {...} void foo() { auto a = new A; a.f(1); } and then some day, the author of std.somemodule now decides he wants to add a member function f(int). All of a sudden, all places you use a.f now mean something else (dangerous). This is unacceptable. It's also unacceptable for a.f to call your f (confusing/ambiguous, sometimes a.f means your f, sometimes it means the member), or to have the compiler error (annoying). I don't see any good solution to this, except to say, "nope, you just gotta write f(a, 1), tough shit." I really don't understand the benefit of allowing a.f instead of f(a), it's a *trivial* gain at best. Note that if I see your code, I'm going straight to the definition of A to try and find the f member function. If I can't find it there, with UFC syntax, it could be anywhere. OTOH, adding member functions to types that don't allow members, although equally as trivial (find(str, "hi") vs. str.find("hi") ), at least is not ambiguous :) -Steve
Feb 03 2011
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Thursday, February 03, 2011 11:15:11 Michel Fortin wrote:
 On 2011-02-03 13:42:30 -0500, Jonathan M Davis <jmdavisProg gmx.com> said:
 On Thursday, February 03, 2011 09:54:44 Michel Fortin wrote:
 On 2011-02-03 12:43:12 -0500, Daniel Gibson <metalcaedes gmail.com> said:
 Am 03.02.2011 15:57, schrieb Michel Fortin:
 On 2011-02-02 23:48:15 -0500, %u <dflgkd sgjds.com> said:
 When implemented, will uniform call syntax work for the "this"
 object even if not specified?
 
 For example, will foo() get called in the following example?
 
 void foo(A a, int b) {}
 
 class A {
 void test() {
 this.foo(10);
 foo(10);
 }
 }

I think it should work.

I think foo(10) should *not* be equivalent to foo(this, 10).

Personally, I'm not sure whether the uniform call syntax will be this much useful or not, but if it gets implemented I think foo(10) should be equivalent to foo(this, 10) in the case above. That said, it should not be ambiguous: if there is a member function foo and a global function foo and both matches the call, it's ambiguous and it should be an error. Can this work in practice? We probably won't know until we have an implementation to play with.

Except that if you have both a member function foo and a free function foo, how can you tell the compiler which to use?

Indeed, that's a problem. The solution is to sidestep the problem. :-) We just have to disallow declaring a module-level function and a class-level function with the same name and the same parameter types (including 'this' in the parameters). void foo(A a, int i); class A { void foo(int i); // error, same as foo(A, int) } If the module-level function is in a different module, then you can use the module name to disambiguate if necessary. If they're in the same module, the compiler catches the error during semantic analysis of the module. I'm not too sure how disruptive this change would be to existing code. I'm under the impression that this situation is rare, but I can't say for sure.

I take it then that you're suggesting that the compiler automatically select the member function when the conflicting free function is a separate module? What about if the class or struct was declared in a separate module but the free function is in the current module? You're back to the same problem again. The only solution I see is for the compiler to just always pick the member function if there's an ambiguity, but I don't know if that will cause other problems or not. It _could_ change which function is called if the member function disappears. Still, I don't see what else you we do, unless we tried to say that you couldn't declare a free function with the same name as a member function that you use in that module and which conflicted with that member function, but that seems awfully restrictive. And then, of course, when you add alias this and opDispatch into the mix... There are certain cases where UFCS looks _really_ appealing if not outright necesarry, but it could get ugly in some cases. I'm not sure that alias this and opDispatch really play nicely together at this point (I rarely use either). Adding UFCS on top of that would just make it worse. So, UFCS is _really_ appealing, but I don't know if it can be reasonably done. - Jonathan M Davis
Feb 03 2011
prev sibling next sibling parent spir <denis.spir gmail.com> writes:
On 02/03/2011 08:15 PM, Michel Fortin wrote:
 On 2011-02-03 13:42:30 -0500, Jonathan M Davis <jmdavisProg gmx.com> said:

 Except that if you have both a member function foo and a free function foo, how
 can you tell the compiler which to use?

Indeed, that's a problem. The solution is to sidestep the problem. :-) We just have to disallow declaring a module-level function and a class-level function with the same name and the same parameter types (including 'this' in the parameters). void foo(A a, int i); class A { void foo(int i); // error, same as foo(A, int) } If the module-level function is in a different module, then you can use the module name to disambiguate if necessary. If they're in the same module, the compiler catches the error during semantic analysis of the module. I'm not too sure how disruptive this change would be to existing code. I'm under the impression that this situation is rare, but I can't say for sure.

How can you propose this, Michel? Complexify the language, and any implementation, just for a non-feature that makes code very hard to decode, by requiring a double mental rewriting operation: foo(i) --> this.foo(i) --> foo(this,i) what advantage does this feature provide? None. What additional drawback: see thread above. Denis -- _________________ vita es estrany spir.wikidot.com
Feb 03 2011
prev sibling next sibling parent spir <denis.spir gmail.com> writes:
On 02/03/2011 08:43 PM, Steven Schveighoffer wrote:
 The more I think about it (and read discussions like this), the more I think
 that uniform call syntax should only be allowed for types which cannot declare
 member functions (arrays, primitives, enums, etc.).  Otherwise, we introduce
 new ambiguities that serve little purpose except annoyance.

More precisely, it should work with all types in position of first parameter, but using implicit this like with member functions. This is ok: void f (C c, I i) {...} auto c = new C(); I i; c.f(i); Don't you think so? The case of member functions using implicit this requires a double rewrite: f(i) --> this.f(i) --> f(this,i) The second one corresponds to universal func call; but the first one is the automagic implicit this which, strictly speaking, should only work for members of the given type. f not beeing a member, the magic should not apply here. Note: if g is a member functon, then g(i) already works. No ambiguity, no complication of the language, no headache for Walter et all, no unreadable code, no recurring issue on D-learn ;-)
 If I have a function foo(A) and A has a member foo(), then foo(myA) should call
 the free function, myA.foo() should call the member.  If this doesn't continue
 to be correct, we have taken a step backwards.

Agreed. Denis -- _________________ vita es estrany spir.wikidot.com
Feb 03 2011
prev sibling parent spir <denis.spir gmail.com> writes:
On 02/03/2011 10:39 PM, Steven Schveighoffer wrote:
 On Thu, 03 Feb 2011 16:05:01 -0500, spir <denis.spir gmail.com> wrote:

 On 02/03/2011 08:43 PM, Steven Schveighoffer wrote:
 The more I think about it (and read discussions like this), the more I think
 that uniform call syntax should only be allowed for types which cannot declare
 member functions (arrays, primitives, enums, etc.). Otherwise, we introduce
 new ambiguities that serve little purpose except annoyance.

More precisely, it should work with all types in position of first parameter, but using implicit this like with member functions. This is ok: void f (C c, I i) {...} auto c = new C(); I i; c.f(i); Don't you think so?

But what if C also defines f? I don't think c.f(i) should fail in that case. If it doesn't fail, then surprises/hijacking will occur (hey, I thought I was calling my f, what gives?!). You could write code like this: import std.somemodule : A; void f(A a, int i) {...} void foo() { auto a = new A; a.f(1); } and then some day, the author of std.somemodule now decides he wants to add a member function f(int). All of a sudden, all places you use a.f now mean something else (dangerous). This is unacceptable. It's also unacceptable for a.f to call your f (confusing/ambiguous, sometimes a.f means your f, sometimes it means the member), or to have the compiler error (annoying). I don't see any good solution to this, except to say, "nope, you just gotta write f(a, 1), tough shit."

Right, I agree.
 I really don't understand the benefit of allowing a.f instead of f(a), it's a
 *trivial* gain at best. Note that if I see your code, I'm going straight to the
 definition of A to try and find the f member function. If I can't find it
 there, with UFC syntax, it could be anywhere.

Ditto.
 OTOH, adding member functions to types that don't allow members, although
 equally as trivial (find(str, "hi") vs. str.find("hi") ), at least is not
 ambiguous :)

That's what I liked in UFCS. But more for conceptual & practical reasons: the (pseudo)type name, just like in OO, acts like a namespace englobing all related functionality. What I would love for Phobos; ideally, this would come with a module system designed in such a way that after: import T; I get access to *all* functionality for elements of type T, using syntax like: t.f(...); (Wherever f is actually implemented; obviously should be in T.d, but this seems to be an Utopia in Phobos.) Denis -- _________________ vita es estrany spir.wikidot.com
Feb 03 2011