www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Is property implementable?

reply Michel Fortin <michel.fortin michelf.com> writes:
Consider that currently, using an array as a range is implemented this way:

	int front(int[] array) {
		return array[0];
	}

	int[] array = [1,2,3];
	auto e = array.front;

Currently, this work fine because the compiler treats "array.front" and 
"array.front()" as the same thing, they're both rewritten as 
"front(array)".

Now consider the world of the future where only functions marked with 
 property can use the property syntax, and only functions *not* marked 
with  property can use the function call syntax. Now, for "array.front" 
to work you'd have to label "front" with  property, like this:

	 property int front(int[] array) {
		return array[0];
	}

The problem is that now, for "array.front" to work it'd have to be 
rewritten as "front = array", which does not make any sense.

So what to do? And also, what happens if you want to implement a setter 
for "array.front = 1"?

-- 
Michel Fortin
michel.fortin michelf.com
http://michelf.com/
Mar 02 2011
next sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Wednesday, March 02, 2011 13:49:40 Michel Fortin wrote:
 Consider that currently, using an array as a range is implemented this way:
 
 	int front(int[] array) {
 		return array[0];
 	}
 
 	int[] array = [1,2,3];
 	auto e = array.front;
 
 Currently, this work fine because the compiler treats "array.front" and
 "array.front()" as the same thing, they're both rewritten as
 "front(array)".
 
 Now consider the world of the future where only functions marked with
  property can use the property syntax, and only functions *not* marked
 with  property can use the function call syntax. Now, for "array.front"
 to work you'd have to label "front" with  property, like this:
 
 	 property int front(int[] array) {
 		return array[0];
 	}
 
 The problem is that now, for "array.front" to work it'd have to be
 rewritten as "front = array", which does not make any sense.
 
 So what to do? And also, what happens if you want to implement a setter
 for "array.front = 1"?

I would expect the compiler to be smart enough to combine the fact that you can call functions on arrays as if they were member functions and property to allow you to deal with any property as if it were a member of the array. So, the fact that the property is declared external to the array should be irrelevant. It should work just the same as any property would on a user-defined type. - Jonathan M Davis
Mar 02 2011
parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2011-03-02 17:14:30 -0500, Jonathan M Davis <jmdavisProg gmx.com> said:

 I would expect the compiler to be smart enough to combine the fact that you can
 call functions on arrays as if they were member functions and  property 
 to allow
 you to deal with any property as if it were a member of the array. So, the fact
 that the property is declared external to the array should be irrelevant. It
 should work just the same as any property would on a user-defined type.

The basic problem is that if you declare this function: property int front(int[] array); you can interpret this declaration in two ways. The standard way, where the function behaves as a setter for the scope it belongs in: front = array; and the "array member" way, where the function behaves as a getter for the property of an array: array.front; How is the compiler supposed to determine which one of the two you meant (setter or getter) when you declared the function? -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Mar 02 2011
parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2011-03-02 20:56:41 -0500, Jonathan M Davis <jmdavisProg gmx.com> said:

 I would argue that properties make no sense unless they have a "this" parameter
 to work.

I would argue that properties make sense everywhere variables make sense. Why should they be restricted to classes and structs? -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Mar 02 2011
parent reply Bekenn <leaveme alone.com> writes:
On 3/2/2011 6:36 PM, Jonathan M Davis wrote:
 Because a property must be a property _of_ something. It's essentially an
 abstraction of a member variable. It allows you to use a function as if it were
 a member variable. That's its whole purpose.

Very much agreed. I'm not sure I'm familiar with UFCS; can you point me to some documentation/discussion on it?
Mar 02 2011
parent reply Bekenn <leaveme alone.com> writes:
On 3/2/2011 8:41 PM, Jonathan M Davis wrote:
 Uniform Function Call Syntax. It means that _any_ type could have functions
 called on it as if they were member functions of that type. e.g.

OK, thanks. This sounds like the sort of thing that should require an annotation in the declaration...
Mar 02 2011
next sibling parent reply %u <wfunction hotmail.com> writes:
 Well, it wouldn't be universal then. For a function to be treated

function call syntax isn't supposed to require an annotation any more than calling a function on an array as if it were a member function requires an annotation. It's supposed to work with any function. So are you saying a hypothetical function like char[] strcpy(char[] destination, const char[] source); should be callable like: myString.strcpy(myString2); ? Isn't that asking for disaster, since it does the exact opposite of what the user expects (since the source and destination are switched)? I think we need an annotation for this (even for arrays), since otherwise it's too easy to go wrong.
Mar 02 2011
parent reply Jacob Carlborg <doob me.com> writes:
On 2011-03-03 08:16, Jonathan M Davis wrote:
 On Wednesday 02 March 2011 23:12:43 %u wrote:
 Well, it wouldn't be universal then. For a function to be treated

as a property, it would require an annotation, but universal function call syntax isn't supposed to require an annotation any more than calling a function on an array as if it were a member function requires an annotation. It's supposed to work with any function. So are you saying a hypothetical function like char[] strcpy(char[] destination, const char[] source); should be callable like: myString.strcpy(myString2); ? Isn't that asking for disaster, since it does the exact opposite of what the user expects (since the source and destination are switched)? I think we need an annotation for this (even for arrays), since otherwise it's too easy to go wrong.

How are the destination and source switched? The arguments are in the exact same order, only now one of them is to the left of the function name. It's been like this with arrays for a long time. It's not going away, and it's not going to require an annotation. That would be a big change to the language, and the ability to call arrays like this is well-liked and often-used. And a number of people definitely want uniform function call syntax to be implemented so that it works for all types. I don't see what's easy to go wrong with this syntax. It's quite straightforward. It's been around for quite a while. And it's well-liked. It's not going to be changed. The only question is whether it's ever going to be implemented for types in general. - Jonathan M Davis

What I think he means is, if you see a function call like this: myString.strcpy(myString2); You would expect "myString" to be the source and "myString2" the destination. -- /Jacob Carlborg
Mar 03 2011
next sibling parent Jacob Carlborg <doob me.com> writes:
On 2011-03-03 17:25, Jonathan M Davis wrote:
 On Thursday 03 March 2011 01:31:38 Jacob Carlborg wrote:
 On 2011-03-03 08:16, Jonathan M Davis wrote:
 On Wednesday 02 March 2011 23:12:43 %u wrote:
 Well, it wouldn't be universal then. For a function to be treated

as a property, it would require an annotation, but universal function call syntax isn't supposed to require an annotation any more than calling a function on an array as if it were a member function requires an annotation. It's supposed to work with any function. So are you saying a hypothetical function like char[] strcpy(char[] destination, const char[] source); should be callable like: myString.strcpy(myString2); ? Isn't that asking for disaster, since it does the exact opposite of what the user expects (since the source and destination are switched)? I think we need an annotation for this (even for arrays), since otherwise it's too easy to go wrong.

How are the destination and source switched? The arguments are in the exact same order, only now one of them is to the left of the function name. It's been like this with arrays for a long time. It's not going away, and it's not going to require an annotation. That would be a big change to the language, and the ability to call arrays like this is well-liked and often-used. And a number of people definitely want uniform function call syntax to be implemented so that it works for all types. I don't see what's easy to go wrong with this syntax. It's quite straightforward. It's been around for quite a while. And it's well-liked. It's not going to be changed. The only question is whether it's ever going to be implemented for types in general. - Jonathan M Davis

What I think he means is, if you see a function call like this: myString.strcpy(myString2); You would expect "myString" to be the source and "myString2" the destination.

But that's not how the function is written. The left parameter is the destination. If myString.strcpy(myString2) is confusing, I would expect strcpy(myString, myString2) to be just as confusing. I don't see how using the member function call syntax like that makes it any more confusing. Not to mention, functions with a source and destination like that end up in both orders all the time, so I don't think that you can generally expect it in one order or the other anyway. - Jonathan M Davis

I think strcpy(myString, myString2) would be confusing as well but I think myString.strcpy(myString2) is a little more confusing than the other syntax. -- /Jacob Carlborg
Mar 04 2011
prev sibling next sibling parent reply Kevin Bealer <kevindangerbealer removedanger.gmail.com> writes:
== Quote from Jonathan M Davis (jmdavisProg gmx.com)'s article
 But that's not how the function is written. The left parameter is the
 destination. If myString.strcpy(myString2) is confusing, I would expect
 strcpy(myString, myString2) to be just as confusing. I don't see how using the
 member function call syntax like that makes it any more confusing. Not to
 mention, functions with a source and destination like that end up in both
orders
 all the time, so I don't think that you can generally expect it in one order or
 the other anyway.
 - Jonathan M Davis

I personally think strcpy got it wrong anyway -- everyone says 'input and output', not 'output and input', so it's weird to think of the output mentioned first. It's as if you wrote: // everyone who uses this will get the argument order wrong at least once int everythingNice(int spice, int sugar); But that's a side note... Personally, I think the UFCS for anything but arrays is an engraved invitation to function hijacking. Imagine defining this in your code: int find(T, U)(T a, U b) { ... } class Foo { int find(double x, char[] y); }; Foo x; x.find(a, "hello"); // would this call the template or the method? Whoops -- now every class, structure, and array in your program has a method called find() that hijacks any arguments that don't exactly match an existing function. If you call x.find(), you can't know if you are getting the method in the class or a free function that got sucked in because of this syntax. Of course if you get the signature exactly right it probably prefers the method over the free function. Arrays are special because the only methods they have are *built in* methods. Thus, a basic understanding of the language makes it possible to avoid the ambiguity. But implementing UFCS for other types is a problem. If I do "a.getIndex(5)" and the compiler says there is no method getIndex on class A, this is a gift --- it is almost always the case that this is an error. I doubt I ever want getIndex(A, int) but wrote A.getIndex(5) or where I wanted f(A, int) but wrote a.f(5). If you want another method in your class *add it to your class*. The reason it makes sense to have it for arrays is so that you can define common methods across all your types like "serialize" method or "toString" or "deepCopy" and know what these methods do. Then you can use them in templates. Arrays can't be subclassed to add the methods so you need a way to produce the same syntax as a method call. Since arrays can't be derived from, UFCS fills in a real gap and doesn't mask existing or future methods unless the interface for arrays changes which is rare, we hope. Allowing it for other types doesn't fill a gap, it creates an overlap/ambiguity. It doesn't let you do anything you can't already do. It's like a built-in alias this that you can't turn off and that matches across all the free functions of the world if I'm understanding the matching correctly. In most cases you can also use functions (for deepCopy etc) to work with both classes and arrays, but this doesn't work with virtual functions, so the method syntax is nicer for that. So UFCS is justified to make arrays 'look like classes' to template code. But it's not justified in my opinion to make classes look like classes with one more method. Kevin
Mar 04 2011
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 3/4/11 2:55 AM, spir wrote:
 Unfortunately, this is not possible for structs (the 'alias this' hack
 is not subtyping (*), it's plain delegation instead).

Subtyping means many things to many people. 'alias this' is "coercive subtyping" by a commonly-accepted definition.
 The absence of
 struct subtyping is a big drawback. Why is it so? (**)

What would be the sketch of an alternative design?
 Denis

 (*) There is no common supertype, so one cannot have a func or
 collection accept both the original type and one or more customized
 types. And indeed there is no method dispatch.

If a type A uses alias this to subtype type B, the relationship is A <: B. Indeed there is no method dispatch, but such is not needed to discuss subtyping.
 (**) Some OO languages have struct-like types (stack-allocated,
 value-semantics) *with* subtyping. See eg Oberon for a wonderful example.

If you could point a link to such a treatment that doesn't require absorbing most of Oberon, that would be great. Thanks! Andrei
Mar 04 2011
prev sibling parent spir <denis.spir gmail.com> writes:
On 03/04/2011 09:31 AM, Kevin Bealer wrote:
 == Quote from Jonathan M Davis (jmdavisProg gmx.com)'s article
 But that's not how the function is written. The left parameter is the
 destination. If myString.strcpy(myString2) is confusing, I would expect
 strcpy(myString, myString2) to be just as confusing. I don't see how using the
 member function call syntax like that makes it any more confusing. Not to
 mention, functions with a source and destination like that end up in both
orders
 all the time, so I don't think that you can generally expect it in one order or
 the other anyway.
 - Jonathan M Davis

I personally think strcpy got it wrong anyway -- everyone says 'input and output', not 'output and input', so it's weird to think of the output mentioned first.

Yes, I really agree, but (unfortunately?) we write target = source;
 It's as if you wrote:

 // everyone who uses this will get the argument order wrong at least once
 int everythingNice(int spice, int sugar);

 But that's a side note...

 Personally, I think the UFCS for anything but arrays is an engraved invitation
to
 function hijacking.  Imagine defining this in your code:

 int find(T, U)(T a, U b)
 {
      ...
 }

 class Foo {
      int find(double x, char[] y);
 };

 Foo x;
 x.find(a, "hello"); // would this call the template or the method?

 Whoops -- now every class, structure, and array in your program has a method
 called find() that hijacks any arguments that don't exactly match an existing
 function.  If you call x.find(), you can't know if you are getting the method
in
 the class or a free function that got sucked in because of this syntax.  Of
course
 if you get the signature exactly right it probably prefers the method over the
 free function.  Arrays are special because the only methods they have are
*built
 in* methods.  Thus, a basic understanding of the language makes it possible to
 avoid the ambiguity.  But implementing UFCS for other types is a problem.

 If I do "a.getIndex(5)" and the compiler says there is no method getIndex on
class
 A, this is a gift --- it is almost always the case that this is an error.  I
doubt
 I ever want getIndex(A, int) but wrote A.getIndex(5) or where I wanted f(A,
int)
 but wrote a.f(5).

 If you want another method in your class *add it to your class*.  The reason it
 makes sense to have it for arrays is so that you can define common methods
across
 all your types like "serialize" method or "toString" or "deepCopy" and know
what
 these methods do.  Then you can use them in templates.  Arrays can't be
subclassed
 to add the methods so you need a way to produce the same syntax as a method
call.

 Since arrays can't be derived from, UFCS fills in a real gap and doesn't mask
 existing or future methods unless the interface for arrays changes which is
rare,
 we hope.  Allowing it for other types doesn't fill a gap, it creates an
 overlap/ambiguity.  It doesn't let you do anything you can't already do.  It's
 like a built-in alias this that you can't turn off and that matches across all
the
 free functions of the world if I'm understanding the matching correctly.

 In most cases you can also use functions (for deepCopy etc) to work with both
 classes and arrays, but this doesn't work with virtual functions, so the method
 syntax is nicer for that.  So UFCS is justified to make arrays 'look like
classes'
 to template code.  But it's not justified in my opinion to make classes look
like
 classes with one more method.

I was a supporter of UFCS, but my opinion starts to change. The main point for me was to allow client code enhance structs/classes according to specific needs. Eg you import a stream class and add it a 'serialize' pseudo-method. But now I start to think 1. libraries should be designed in such that this need is rare enough 2. one can and should subtype, thus making clear it's a new version Unfortunately, this is not possible for structs (the 'alias this' hack is not subtyping (*), it's plain delegation instead). The absence of struct subtyping is a big drawback. Why is it so? (**) Denis (*) There is no common supertype, so one cannot have a func or collection accept both the original type and one or more customized types. And indeed there is no method dispatch. (**) Some OO languages have struct-like types (stack-allocated, value-semantics) *with* subtyping. See eg Oberon for a wonderful example. -- _________________ vita es estrany spir.wikidot.com
Mar 04 2011
prev sibling parent Bekenn <leaveme alone.com> writes:
On 3/2/2011 9:21 PM, Jonathan M Davis wrote:
 Well, it wouldn't be universal then.

Agreed, and really, I don't have a problem with it being universal. I'd prefer an annotation, but it's not that big a deal. I'm just thinking of the following situation: lib1.di: class Lib1Class { ... version (2) { void bar(); } } lib2.di: import lib1; void bar(Lib1Class l1c); main.d: import lib1; import lib2; void main() { Lib1Class foo; foo.bar(); } Through no fault of the caller or of lib2, foo.bar() goes from one completely valid meaning to another when lib1 is versioned. Lib1 and lib2 could be from different authors, so lib1's author had no idea he'd just hijacked a function call. With an annotation, the compiler could at least spit out a warning for lib2.di. (I would be absolutely opposed to a warning for line 7 of main; that should be an unambiguous method call in version 2.) Anyway, I'm just thinking out loud (erm, in text); I'm sure this has all been debated already.
Mar 02 2011
prev sibling next sibling parent reply dsimcha <dsimcha yahoo.com> writes:
== Quote from Michel Fortin (michel.fortin michelf.com)'s article
 Consider that currently, using an array as a range is implemented this way:
 	int front(int[] array) {
 		return array[0];
 	}
 	int[] array = [1,2,3];
 	auto e = array.front;
 Currently, this work fine because the compiler treats "array.front" and
 "array.front()" as the same thing, they're both rewritten as
 "front(array)".
 Now consider the world of the future where only functions marked with
  property can use the property syntax, and only functions *not* marked
 with  property can use the function call syntax. Now, for "array.front"
 to work you'd have to label "front" with  property, like this:
 	 property int front(int[] array) {
 		return array[0];
 	}
 The problem is that now, for "array.front" to work it'd have to be
 rewritten as "front = array", which does not make any sense.
 So what to do? And also, what happens if you want to implement a setter
 for "array.front = 1"?

First of all, I thought that we had decided that DMD's enforcement of property would be loose, i.e. it should only be used to disambiguate in cases where ambiguity exists (for example, when returning delegates). As long as there's no ambiguity, property should simply have no effect.
Mar 02 2011
next sibling parent Michel Fortin <michel.fortin michelf.com> writes:
On 2011-03-02 17:29:18 -0500, dsimcha <dsimcha yahoo.com> said:

 First of all, I thought that we had decided that DMD's enforcement of  property
 would be loose, i.e. it should only be used to disambiguate in cases where
 ambiguity exists (for example, when returning delegates).  As long as 
 there's no
 ambiguity,  property should simply have no effect.

Functionalities which have no effect except in some edge cases no one fully understand are just begging to be misused. I'm not exactly sure what was decided or even if something was decided, but what you're proposing here doesn't sound like a good idea to me. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Mar 02 2011
prev sibling parent "Nick Sabalausky" <a a.a> writes:
"dsimcha" <dsimcha yahoo.com> wrote in message 
news:ikmgbu$2fmc$1 digitalmars.com...
 First of all, I thought that we had decided that DMD's enforcement of 
  property
 would be loose, i.e. it should only be used to disambiguate in cases where
 ambiguity exists (for example, when returning delegates).  As long as 
 there's no
 ambiguity,  property should simply have no effect.

That's the first I've ever heard of it being intended to work that way.
Mar 02 2011
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Wednesday, March 02, 2011 16:57:42 Michel Fortin wrote:
 On 2011-03-02 17:14:30 -0500, Jonathan M Davis <jmdavisProg gmx.com> said:
 I would expect the compiler to be smart enough to combine the fact that
 you can call functions on arrays as if they were member functions and
  property to allow
 you to deal with any property as if it were a member of the array. So,
 the fact that the property is declared external to the array should be
 irrelevant. It should work just the same as any property would on a
 user-defined type.

The basic problem is that if you declare this function: property int front(int[] array); you can interpret this declaration in two ways. The standard way, where the function behaves as a setter for the scope it belongs in: front = array; and the "array member" way, where the function behaves as a getter for the property of an array: array.front; How is the compiler supposed to determine which one of the two you meant (setter or getter) when you declared the function?

I would argue that properties make no sense unless they have a "this" parameter to work. In the case of arrays, the first parameter is effectively the "this" parameter - it just isn't invisible like it is with a class or struct. I don't think that a property which isn't called on something makes any sense at all. So, front = array; makes no sense at all and wouldn't even be considered by the compiler. In the case of a struct or class, a getter property is a function which takes nothing (except the invisible this parameter) and returns a value, whereas a setter property is a function which returns nothing but takes only the value to set (and the invisible this parameter) - or a property function is both a getter and a setter but takes nothing (except the invisible this parameter) and returns a ref. In the case of arrays or uniform function call syntax (if it ever gets implemented), a getter property is a function at module scope which takes only an array (or whatever the type is if it's using UFCS) and returns a value, whereas a setter property is a function at module scope which returns nothing and takes only the array (or whatever type is if it's using UFCS) and the value to set - or a property function is both a getter and a setter but takes only the array (or whatever the type is if it's using UFCS) and returns a ref. I don't see any ambiguity here. A property makes no sense unless it's a property _of_ something. - Jonathan M Davis
Mar 02 2011
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Wednesday 02 March 2011 18:02:18 Michel Fortin wrote:
 On 2011-03-02 20:56:41 -0500, Jonathan M Davis <jmdavisProg gmx.com> said:
 I would argue that properties make no sense unless they have a "this"
 parameter to work.

I would argue that properties make sense everywhere variables make sense. Why should they be restricted to classes and structs?

Because a property must be a property _of_ something. It's essentially an abstraction of a member variable. It allows you to use a function as if it were a member variable. That's its whole purpose. What's a module/global variable a property of? The module? I really don't think that that makes sense. A module/global variable is not a member variable, and the use of mutable module/global variables is _not_ something which should be encouraged anyway. So, conceptually, it doesn't really make sense for a "property" to be on a module, and practically-speaking, I don't really see any benefit at all. Not to mention, if it adds a layer of ambiguity (as you've brought up), then it _really_ doesn't make sense to do that. Regardless, I don't think that it makes any sense whatsoever for a property to be on anything other than a user-defined type or a type which is acting like one (like arrays or something called with UFCS). A property is an abstraction for a member variable, not just any type of variable. - Jonathan M Davis
Mar 02 2011
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Wednesday 02 March 2011 20:17:48 Bekenn wrote:
 On 3/2/2011 6:36 PM, Jonathan M Davis wrote:
 Because a property must be a property _of_ something. It's essentially an
 abstraction of a member variable. It allows you to use a function as if
 it were a member variable. That's its whole purpose.

Very much agreed. I'm not sure I'm familiar with UFCS; can you point me to some documentation/discussion on it?

Uniform Function Call Syntax. It means that _any_ type could have functions called on it as if they were member functions of that type. e.g. "hello".find("e"); or 2.max(7); That means that you could use the same function call syntax with all types instead of just user-defined types. However, at present, in only works with arrays. It may end up being implemented for all types at some point, or it may not. But if it _is_ implemented, then any type could have the same issue with property functions that Michel Fortin brought up. But again, for now (and possibly forever), it only works with arrays. - Jonathan M Davis
Mar 02 2011
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Wednesday 02 March 2011 21:09:09 Bekenn wrote:
 On 3/2/2011 8:41 PM, Jonathan M Davis wrote:
 Uniform Function Call Syntax. It means that _any_ type could have
 functions called on it as if they were member functions of that type.
 e.g.

OK, thanks. This sounds like the sort of thing that should require an annotation in the declaration...

Well, it wouldn't be universal then. For a function to be treated as a property, it would require an annotation, but universal function call syntax isn't supposed to require an annotation any more than calling a function on an array as if it were a member function requires an annotation. It's supposed to work with any function. - Jonathan M davis
Mar 02 2011
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Wednesday 02 March 2011 23:12:43 %u wrote:
 Well, it wouldn't be universal then. For a function to be treated

as a property, it would require an annotation, but universal function call syntax isn't supposed to require an annotation any more than calling a function on an array as if it were a member function requires an annotation. It's supposed to work with any function. So are you saying a hypothetical function like char[] strcpy(char[] destination, const char[] source); should be callable like: myString.strcpy(myString2); ? Isn't that asking for disaster, since it does the exact opposite of what the user expects (since the source and destination are switched)? I think we need an annotation for this (even for arrays), since otherwise it's too easy to go wrong.

How are the destination and source switched? The arguments are in the exact same order, only now one of them is to the left of the function name. It's been like this with arrays for a long time. It's not going away, and it's not going to require an annotation. That would be a big change to the language, and the ability to call arrays like this is well-liked and often-used. And a number of people definitely want uniform function call syntax to be implemented so that it works for all types. I don't see what's easy to go wrong with this syntax. It's quite straightforward. It's been around for quite a while. And it's well-liked. It's not going to be changed. The only question is whether it's ever going to be implemented for types in general. - Jonathan M Davis
Mar 02 2011
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Wednesday 02 March 2011 23:21:21 Bekenn wrote:
 On 3/2/2011 9:21 PM, Jonathan M Davis wrote:
 Well, it wouldn't be universal then.

Agreed, and really, I don't have a problem with it being universal. I'd prefer an annotation, but it's not that big a deal. I'm just thinking of the following situation: lib1.di: class Lib1Class { ... version (2) { void bar(); } } lib2.di: import lib1; void bar(Lib1Class l1c); main.d: import lib1; import lib2; void main() { Lib1Class foo; foo.bar(); } Through no fault of the caller or of lib2, foo.bar() goes from one completely valid meaning to another when lib1 is versioned. Lib1 and lib2 could be from different authors, so lib1's author had no idea he'd just hijacked a function call. With an annotation, the compiler could at least spit out a warning for lib2.di. (I would be absolutely opposed to a warning for line 7 of main; that should be an unambiguous method call in version 2.) Anyway, I'm just thinking out loud (erm, in text); I'm sure this has all been debated already.

No, not all of the details have been ironed out. It's not even sure that it's ever going to be implemented. However, the whole point of the feature of universal function call syntax is to have a universal syntax for calling functions. It's supposed to work for all types. So, annotations defeat the purpose. It has been suggested that UFCS not affect structs or classes (which would fix this case). But even if UFCS _did_ affect structs and classes, in this particular case, if both functions existed, it would either result in an error due to the ambiguity, or it would simply call the member function. Regardless, with the current state of D, if the functions in imported libraries change, then the functions being called could change. There's no way around that. It's just that D has rules specifically designed to limit the possibility of function hijacking. If UFCS _is_ ever implemented, then there _are_ definitely some issues which will have be ironed out, but you're not going to end up with annotations regardless. Arrays don't work that way and annotations would defeat the purpose of the feature. However, it's issues like this which could lead to UFCS never being implemented even if Walter definitely decides to take a stab at it. - Jonathan M Davis
Mar 03 2011
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Wed, 02 Mar 2011 16:49:40 -0500, Michel Fortin  
<michel.fortin michelf.com> wrote:

 Consider that currently, using an array as a range is implemented this  
 way:

 	int front(int[] array) {
 		return array[0];
 	}

 	int[] array = [1,2,3];
 	auto e = array.front;

 Currently, this work fine because the compiler treats "array.front" and  
 "array.front()" as the same thing, they're both rewritten as  
 "front(array)".

 Now consider the world of the future where only functions marked with  
  property can use the property syntax, and only functions *not* marked  
 with  property can use the function call syntax. Now, for "array.front"  
 to work you'd have to label "front" with  property, like this:

 	 property int front(int[] array) {
 		return array[0];
 	}

 The problem is that now, for "array.front" to work it'd have to be  
 rewritten as "front = array", which does not make any sense.

 So what to do? And also, what happens if you want to implement a setter  
 for "array.front = 1"?

I brought this up a while ago, look for "Uniform Function Call syntax for properties" possible solution? int front( property int[] array) Ugly, but it may make things unambiguous. Probably would have to require this for all properties on arrays, even the unambiguous cases. This is really the only ambiguous case. These should be unambiguous: property int[] front(); property void front(int[] array, int value); Although I don't like it, I think Jonathan's solution (just disable settable properties in the global space) makes things the least painless. Yes, you lose settable properties from modules, static class/struct namespace, but it's just sugar. I think we can probably live without it. I agree with Jonathan that most of the properties in D are members of classes/structs. -Steve
Mar 03 2011
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Thursday 03 March 2011 01:31:38 Jacob Carlborg wrote:
 On 2011-03-03 08:16, Jonathan M Davis wrote:
 On Wednesday 02 March 2011 23:12:43 %u wrote:
 Well, it wouldn't be universal then. For a function to be treated

as a property, it would require an annotation, but universal function call syntax isn't supposed to require an annotation any more than calling a function on an array as if it were a member function requires an annotation. It's supposed to work with any function. So are you saying a hypothetical function like char[] strcpy(char[] destination, const char[] source); should be callable like: myString.strcpy(myString2); ? Isn't that asking for disaster, since it does the exact opposite of what the user expects (since the source and destination are switched)? I think we need an annotation for this (even for arrays), since otherwise it's too easy to go wrong.

How are the destination and source switched? The arguments are in the exact same order, only now one of them is to the left of the function name. It's been like this with arrays for a long time. It's not going away, and it's not going to require an annotation. That would be a big change to the language, and the ability to call arrays like this is well-liked and often-used. And a number of people definitely want uniform function call syntax to be implemented so that it works for all types. I don't see what's easy to go wrong with this syntax. It's quite straightforward. It's been around for quite a while. And it's well-liked. It's not going to be changed. The only question is whether it's ever going to be implemented for types in general. - Jonathan M Davis

What I think he means is, if you see a function call like this: myString.strcpy(myString2); You would expect "myString" to be the source and "myString2" the destination.

But that's not how the function is written. The left parameter is the destination. If myString.strcpy(myString2) is confusing, I would expect strcpy(myString, myString2) to be just as confusing. I don't see how using the member function call syntax like that makes it any more confusing. Not to mention, functions with a source and destination like that end up in both orders all the time, so I don't think that you can generally expect it in one order or the other anyway. - Jonathan M Davis
Mar 03 2011
prev sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Friday 04 March 2011 00:31:37 Kevin Bealer wrote:
 == Quote from Jonathan M Davis (jmdavisProg gmx.com)'s article
 
 But that's not how the function is written. The left parameter is the
 destination. If myString.strcpy(myString2) is confusing, I would expect
 strcpy(myString, myString2) to be just as confusing. I don't see how
 using the member function call syntax like that makes it any more
 confusing. Not to mention, functions with a source and destination like
 that end up in both orders all the time, so I don't think that you can
 generally expect it in one order or the other anyway.
 - Jonathan M Davis

I personally think strcpy got it wrong anyway -- everyone says 'input and output', not 'output and input', so it's weird to think of the output mentioned first. It's as if you wrote: // everyone who uses this will get the argument order wrong at least once int everythingNice(int spice, int sugar); But that's a side note...

IIRC, the assembly code used by gcc does it one way, and the assembly code used by Microsoft's compiler does it the other way. So, I've dealt with both, and which makes more sense to me depends on how I'm thinking on a particular day. I think that I'd be inclined to do target <- source rather than source -> target if I were choosing, but I've dealt with enough functions that did it both ways that it's not the kind of thing that I tend to assume. It just varies too much.
 Personally, I think the UFCS for anything but arrays is an engraved
 invitation to function hijacking.  Imagine defining this in your code:

If UFCS ever gets implemented, it will have to be in a way which avoids function hijacking, or there's no way that it will ever be implemented. D just plain doesn't support function hijacking. It's smarter/more restrictive than that. So, if UFCS can't be done without avoiding function hijacking, I don't expect it to ever be implemented. That being said, I think that UFCS would potentially be very useful for implementing functions which act like they're part of a struct or class when they're not. For instance, I have one co-worker who is constantly irrated by how few functions C++'s std::string has and wants there to be more, but does not want to use utility functions, and pretty much no one else we work with wants to have a separate string class (as is done far too often in C++). Our current solution is a new string class that simply wraps std::string and allows us to add all of the functions that we need, but it would be much better were it possible to add free functions which could be used like member functions. _That_ is the sort of scenario that I, personally, think that UFCS would be valuable for. I see little to no value in stuff like 7.max(5). Using member function call syntax for arrays is fantastic, but I really don't think that it adds much for other types of primitives. So, if UFCS can't be cleanly implemented for structs and classes, I don't really such much point to it, personally. But regardless, if UFCS is ever to be implemented, it needs to be done in a manner which completely avoids function hijacking or it will be unacceptable - especially to Walter. Who knows whether we'll ever see UFCS or not, but its kinks will have to be worked out before it stands any chance of seeing the light of day. - Jonathan M Davis
Mar 04 2011