www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - UFCS and constructors

reply "monarch_dodra" <monarchdodra gmail.com> writes:
Coming back from learn here. There was an example where somebody 
"accidentally" called a constructor via UFCS. I am kind of 
surprised that it worked. I thought UFCS was for functions only, 
and that constructors (specifically) were off limits.

Am I mistaken? Is UFCS explicitly allowed for constructors? Or 
did we kind of forget to take it into account?

One of the "big" problems with allowing UFCS and constructors is 
that a "." which meant "scope" can now mean "function call". If I 
remember correctly, that is the reason why qualified calls (eg 
'a'.std.uni.toLower()) aren't allowed in UFCS (Am I correct?), 
but with constructors, you get the same problem:

Allow me to demonstrate:

--------
import std.stdio;

struct Bar
{
     struct S
     {
         int i;
     }
     enum j = 1;
}

struct S
{
     int[10] i;
}

void main()
{
     writeln(Bar.S());   (1)
     writeln(Bar.j.S()); (2)
}
--------

(1) is a standard scope call: instantiate a Bar.S. This prints 
"S(0)".
(2) is actually: "get the value j from Bar, and then UFCS 
construct an S using that J". This prints:  "S([1, 1, 1, 1, 1, 1, 
1, 1, 1, 1])"

Furthermore, I find UFCS construction confusing on the grounds 
that there is no actual "constructor function" eg: "this(...)" 
call: This is just aggregate initialization, which looks *very* 
confusing when written that way.

------------------------------------

So to sum up the question: Was UFCS + constructors are really 
desired feature? Was it taken into account? Do we want to keep it?

In particular, the "standard" workaround of "free function 
constructor" (EG "Take" vs "take") would serve much better here.
Jul 02 2013
next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
monarch_dodra:

 Is UFCS explicitly allowed for constructors? Or did we kind of 
 forget to take it into account?
I think it's allowed, but such design decisions should be written in a kind of document... instead of just in the D front end source code. See also: http://d.puremagic.com/issues/show_bug.cgi?id=9857 Bye, bearophile
Jul 02 2013
prev sibling next sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Tuesday, July 02, 2013 09:35:38 monarch_dodra wrote:
 Coming back from learn here. There was an example where somebody
 "accidentally" called a constructor via UFCS. I am kind of
 surprised that it worked. I thought UFCS was for functions only,
 and that constructors (specifically) were off limits.
 
 Am I mistaken? Is UFCS explicitly allowed for constructors? Or
 did we kind of forget to take it into account?
I'm not sure that it was ever decided one way or the other so much as happened into being due to how UFCS was implemented. I know that it's come up before, and folks were arguing on both sides. Personally, I think that it's a horrible idea. - Jonathan M Davis
Jul 02 2013
parent reply "deadalnix" <deadalnix gmail.com> writes:
On Tuesday, 2 July 2013 at 08:16:38 UTC, Jonathan M Davis wrote:
 On Tuesday, July 02, 2013 09:35:38 monarch_dodra wrote:
 Coming back from learn here. There was an example where 
 somebody
 "accidentally" called a constructor via UFCS. I am kind of
 surprised that it worked. I thought UFCS was for functions 
 only,
 and that constructors (specifically) were off limits.
 
 Am I mistaken? Is UFCS explicitly allowed for constructors? Or
 did we kind of forget to take it into account?
I'm not sure that it was ever decided one way or the other so much as happened into being due to how UFCS was implemented. I know that it's come up before, and folks were arguing on both sides. Personally, I think that it's a horrible idea. - Jonathan M Davis
We are 2. that is horrible.
Jul 02 2013
parent Kenji Hara <k.hara.pg gmail.com> writes:
2013/7/2 deadalnix <deadalnix gmail.com>

 On Tuesday, 2 July 2013 at 08:16:38 UTC, Jonathan M Davis wrote:

 On Tuesday, July 02, 2013 09:35:38 monarch_dodra wrote:

 Coming back from learn here. There was an example where somebody
 "accidentally" called a constructor via UFCS. I am kind of
 surprised that it worked. I thought UFCS was for functions only,
 and that constructors (specifically) were off limits.

 Am I mistaken? Is UFCS explicitly allowed for constructors? Or
 did we kind of forget to take it into account?
I'm not sure that it was ever decided one way or the other so much as happened into being due to how UFCS was implemented. I know that it's come up before, and folks were arguing on both sides. Personally, I think that it's a horrible idea. - Jonathan M Davis
We are 2. that is horrible.
I don't know what design decision had been there about it. Historically, there's no restriction against UFCS-callable entity. With 2.030 (released on May 11, 2009) and git head, following code completely works. void foo(int[]) {} void bar(T)(T) {} struct Foo { int[] x; } struct Bar { this(int[]) {} } struct Baz { static opCall(int[]) { return 0; } } int[] function(int[]) fp; int[] delegate(int[]) dg; struct Functor { int opCall(int[]) { return 0; } } Functor fn; void main() { fp = function(int[] x){ return x; }; dg = delegate(int[] x){ return x; }; int[] a; a.foo(); a.bar(); auto x1 = a.Foo(); auto x2 = a.Bar(); auto x3 = a.Baz(); a.fp(); a.dg(); a.fn(); } While improvement of dmd front-end code, I didn't touch it. Yes, I did never designed it... Kenji Hara
Jul 02 2013
prev sibling next sibling parent "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Tuesday, 2 July 2013 at 07:35:39 UTC, monarch_dodra wrote:
 One of the "big" problems with allowing UFCS and constructors 
 is that a "." which meant "scope" can now mean "function call".
Just for the interest - try to guess what following in D may be: a.b = c.d(); This is a good example how overloaded D syntax is.
 --------

 (1) is a standard scope call: instantiate a Bar.S. This prints 
 "S(0)".
 (2) is actually: "get the value j from Bar, and then UFCS 
 construct an S using that J". This prints:  "S([1, 1, 1, 1, 1, 
 1, 1, 1, 1, 1])"

 Furthermore, I find UFCS construction confusing on the grounds 
 that there is no actual "constructor function" eg: "this(...)" 
 call: This is just aggregate initialization, which looks *very* 
 confusing when written that way.

 ------------------------------------

 So to sum up the question: Was UFCS + constructors are really 
 desired feature? Was it taken into account? Do we want to keep 
 it?
I think this worth bug issue. Even if this behavior would be proven to be correct (I guess many wish it wouldn't), bugzilla request could be posted as an ask to provide use case in D documentation site to show this behavior explicitly.
Jul 02 2013
prev sibling next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 07/02/2013 09:35 AM, monarch_dodra wrote:
 Furthermore, I find UFCS construction confusing on the grounds that there is
no actual
 "constructor function" eg: "this(...)" call:  This is just aggregate
initialization,
Aggregate initialization is the job of the constructor. It is a default constructor call.
 which looks *very* confusing when written that way.
I disagree, even though the example appears to be specifically designed to confuse. Actual usage looks like this: import std.stdio, std.bigint void main(){ writeln(2.BigInt ^^ 123456); }
 ------------------------------------
 So to sum up the question: Was UFCS + constructors are really desired
 feature?
UFCS allows foo(a,b) to be written as a.foo(b), if 'foo' is not a member of a.
 Was it taken into account?
I guess so.
 Do we want to keep it?
There is no reason to artificially ban it.
 In particular, the "standard" workaround of "free function constructor"
What is the difference?
 (EG "Take" vs "take") would serve much better here.
"take" is not a "free function constructor".
Jul 02 2013
next sibling parent "monarch_dodra" <monarchdodra gmail.com> writes:
On Tuesday, 2 July 2013 at 12:46:42 UTC, Timon Gehr wrote:
 On 07/02/2013 09:35 AM, monarch_dodra wrote:
 which looks *very* confusing when written that way.
I disagree, even though the example appears to be specifically designed to confuse. Actual usage looks like this: import std.stdio, std.bigint void main(){ writeln(2.BigInt ^^ 123456); }
Yeah... tailored for confusion... that could be the biggest issue actually: I always get surprised when arrays are 1 item initialized, and even more so when done in a struct via aggregate initialization. I'd say *that* was actually the bigger culprit in my example, and the one that lead to my confusion, which I then blamed (or "called wolf") on UFCS "If it wasn't for UFCS, that would have been turned down!" UFCS actually had nothing to do with it. :( Well, thanks for the explanation and debunk.
Jul 02 2013
prev sibling parent reply "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Tuesday, July 02, 2013 14:46:41 Timon Gehr wrote:
 Do we want to keep it?
There is no reason to artificially ban it.
There's nothing artificial about it. Constructors are not normal functions and should not be treated as such. They're fundamentally different from normal functions. Also, in all other cases, UFCS involves using a free function as if it were a member function, so it's incredibly bizarre as well as inconsistent with the rest of UFCS to allow constructors to be used with it. - Jontahan M Davis
Jul 02 2013
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 07/02/2013 07:58 PM, Jonathan M Davis wrote:
 On Tuesday, July 02, 2013 14:46:41 Timon Gehr wrote:
 Do we want to keep it?
There is no reason to artificially ban it.
There's nothing artificial about it. Constructors are not normal functions and should not be treated as such. They're fundamentally different from normal functions. Also, in all other cases, UFCS involves using a free function as if it were a member function, so it's incredibly bizarre as well as inconsistent with the rest of UFCS to allow constructors to be used with it. - Jontahan M Davis
It is an artificial limitation, because you need to add an explicit check after symbol lookup to ban constructors. Analogies are always broken, but the rest of the post reads to me roughly like: "Dogs are not pets, and should not be treated as such. They are fundamentally different from pets. Also, there are no other pets that bark, so it's incredibly bizarre as well as inconsistent with the rest of the notion of a 'pet' to have a pet dog. - Tmion M Gehr " This has happened before. What am I missing?
Jul 02 2013
next sibling parent reply "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Tuesday, 2 July 2013 at 19:47:07 UTC, Timon Gehr wrote:
 Analogies are always broken, but the rest of the post reads to 
 me roughly like:

 "Dogs are not pets, and should not be treated as such.
 They are fundamentally different from pets.

 Also, there are no other pets that bark, so it's incredibly 
 bizarre as well as inconsistent with the rest of the notion of 
 a 'pet' to have a pet dog.

 - Tmion M Gehr
 "
class A { this() {} void foo(){} } A a = new A; // calls ctor, not foo Of course constructors are special because not any function is called upon object construction. Same logic was made when ability to overload some operators was blocked. All operators are, well, operators but you cannot overload all of them. It appears that sometimes it does make sense to restrict operation on some particular elements of the set and sometimes not. Anyway, without final decision on this issue, there would be endless controversy between those who point on commonness of all functions and those who point on peculiarity of some of them.
Jul 02 2013
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 07/02/2013 10:16 PM, Maxim Fomin wrote:
 On Tuesday, 2 July 2013 at 19:47:07 UTC, Timon Gehr wrote:
 Analogies are always broken, but the rest of the post reads to me
 roughly like:

 "Dogs are not pets, and should not be treated as such.
 They are fundamentally different from pets.

 Also, there are no other pets that bark, so it's incredibly bizarre as
 well as inconsistent with the rest of the notion of a 'pet' to have a
 pet dog.

 - Tmion M Gehr
 "
class A { this() {} void foo(){} } A a = new A; // calls ctor, not foo Of course constructors are special because not any function is called upon object construction.
(We are discussing struct constructors.) Of course. I fully agree. Obviously there is a reason why those functions are called constructors. But one could now extend on the original argument, and say that eg. all the overloading rules should be different for constructors, because they are fundamentally different and should under no circumstances be treated like 'normal functions'. Do you see what the point is? This is not a valid way of justifying a breaking language change.
 Same logic
Logics that can prove some equivalent statements are not necessarily equivalent. (In particular, it does not rule out inconsistency of one of them.)
 was made when ability to overload
 some operators was blocked. All operators are, well, operators but you
 cannot overload all of them. It appears that sometimes it does make
 sense to restrict operation on some particular elements of the set and
 sometimes not.
 ...
Such restrictions need to be justified. The justification should make (at least some) sense. There is no point in heuristically designing language features from observations about other language features without applying insight. (Anyway, I do not think that overloading of a fixed set of primitive operators using specially named member functions is a good mechanism for infix notation.)
Jul 02 2013
parent reply "deadalnix" <deadalnix gmail.com> writes:
On Tuesday, 2 July 2013 at 21:38:26 UTC, Timon Gehr wrote:
 Such restrictions need to be justified. The justification 
 should make (at least some) sense. There is no point in 
 heuristically designing language features from observations 
 about other language features without applying insight.
The whole point of UFCS is to be able to provide additional custom "methods" to a object (class or struct). Constructor UFCS don't fulfill that use case. Nothing is removed from the language as factories method can be introduced anyway.
Jul 02 2013
parent reply "bearophile" <bearophileHUGS lycos.com> writes:
deadalnix:

 The whole point of UFCS is to be able to provide additional 
 custom "methods" to a object (class or struct). Constructor 
 UFCS don't fulfill that use case.

 Nothing is removed from the language as factories method can be 
 introduced anyway.
This frames the topic in a wrong way. Constructors are not normal functions, they are special, but functional languages show us that's it's a very good idea to see them as functions. And the original point of UFCS doesn't matter much. What matters is what are the practical disadvantages of allowing UFCSyntax for constructors (like the original post in this thread), and what are their practical advantages/uses (like a handy usage in UFCS chains). Then we take a look at what's the resulting balance and we decide. And such decisions should then become the written specifics of this part of the D design. Bye, bearophile
Jul 02 2013
parent reply "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Wednesday, July 03, 2013 04:00:45 bearophile wrote:
 deadalnix:
 The whole point of UFCS is to be able to provide additional
 custom "methods" to a object (class or struct). Constructor
 UFCS don't fulfill that use case.
 
 Nothing is removed from the language as factories method can be
 introduced anyway.
This frames the topic in a wrong way. Constructors are not normal functions, they are special, but functional languages show us that's it's a very good idea to see them as functions. And the original point of UFCS doesn't matter much. What matters is what are the practical disadvantages of allowing UFCSyntax for constructors (like the original post in this thread), and what are their practical advantages/uses (like a handy usage in UFCS chains). Then we take a look at what's the resulting balance and we decide. And such decisions should then become the written specifics of this part of the D design.
The primary benefit of UFCS is generic code, because if uses UFCS, generic code doesn't have to care whether a function is a member function or a free function (particularly when that can vary drastically depending on what type is used to instantiate the template). Construction is not and cannot be generic. The closest that you could get would be a factory function, which is quite different. But constructors themselves cannot be generic. As such, using constructors with UFCS in generic code just doesn't work, meaning that the only gain that you're getting from using UFCS with constructors is that you get a slightly different syntax that you might like better for one reason or another. But I see no technical reason why it could add any benefit over simply calling the constructor normally. And as such, I think that allowing UFCS to work with constructors is definitely an anti-feature. - Jonathan M Davis
Jul 02 2013
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 07/03/2013 04:12 AM, Jonathan M Davis wrote:
 On Wednesday, July 03, 2013 04:00:45 bearophile wrote:
 deadalnix:
 The whole point of UFCS is to be able to provide additional
 custom "methods" to a object (class or struct). Constructor
 UFCS don't fulfill that use case.

 Nothing is removed from the language as factories method can be
 introduced anyway.
This frames the topic in a wrong way. Constructors are not normal functions, they are special, but functional languages show us that's it's a very good idea to see them as functions. And the original point of UFCS doesn't matter much. What matters is what are the practical disadvantages of allowing UFCSyntax for constructors (like the original post in this thread), and what are their practical advantages/uses (like a handy usage in UFCS chains). Then we take a look at what's the resulting balance and we decide. And such decisions should then become the written specifics of this part of the D design.
The primary benefit of UFCS is generic code, because if uses UFCS, generic code doesn't have to care whether a function is a member function or a free function (particularly when that can vary drastically depending on what type is used to instantiate the template).
No. Generic code has to be careful with UFCS, because every method that is called on a suitable variable via UFCS can be (accidentally) replaced by the client code.
 Construction is not and cannot be generic.
The point is that struct constructors can be generically used like other callables. import std.stdio, std.algorithm; struct S{ int x; } void main(){ auto x = [1,2,3]; writeln(x.map!S); } There is nothing to be gained from subtly breaking this analogy. UFCS can be applied to any callable. You are probably not going to like this, but the following code also works: import std.stdio; struct S{ int opCall(int x){ return x+1; } } S s; void main(){ auto x = 1; writeln(x.s); }
 The closest that you could get would be a factory function, which is
 quite different.  But constructors themselves cannot be generic. As such, using
 constructors with UFCS in generic code just doesn't work, meaning that the
 only gain that you're getting from using UFCS with constructors is that you
 get a slightly different syntax that you might like better for one reason or
 another. But I see no technical reason why it could add any benefit over simply
 calling the constructor normally. And as such, I think that allowing UFCS to
 work with constructors is definitely an anti-feature.
 ...
To be an anti-feature it has to be harmful, not just of less benefit than some other (aspect of the) feature.
Jul 02 2013
next sibling parent reply "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Wednesday, July 03, 2013 04:54:41 Timon Gehr wrote:
 On 07/03/2013 04:12 AM, Jonathan M Davis wrote:
 On Wednesday, July 03, 2013 04:00:45 bearophile wrote:
 deadalnix:
 The whole point of UFCS is to be able to provide additional
 custom "methods" to a object (class or struct). Constructor
 UFCS don't fulfill that use case.
 
 Nothing is removed from the language as factories method can be
 introduced anyway.
This frames the topic in a wrong way. Constructors are not normal functions, they are special, but functional languages show us that's it's a very good idea to see them as functions. And the original point of UFCS doesn't matter much. What matters is what are the practical disadvantages of allowing UFCSyntax for constructors (like the original post in this thread), and what are their practical advantages/uses (like a handy usage in UFCS chains). Then we take a look at what's the resulting balance and we decide. And such decisions should then become the written specifics of this part of the D design.
The primary benefit of UFCS is generic code, because if uses UFCS, generic code doesn't have to care whether a function is a member function or a free function (particularly when that can vary drastically depending on what type is used to instantiate the template).
No. Generic code has to be careful with UFCS, because every method that is called on a suitable variable via UFCS can be (accidentally) replaced by the client code.
That's the whole point! If you absolutely have to be certain that it's not calling a free function, then don't use UFCS, but the primary benefits of UFCS   are making it so that generic code doesn't have to care whether a function is a free function and making it so that types can overload the behavior of free functions (e.g. having a member function find which is optimized for that type and can be used in place of std.algorithm.find).
 Construction is not and cannot be generic.
The point is that struct constructors can be generically used like other callables. import std.stdio, std.algorithm; struct S{ int x; } void main(){ auto x = [1,2,3]; writeln(x.map!S); } There is nothing to be gained from subtly breaking this analogy. UFCS can be applied to any callable. You are probably not going to like this, but the following code also works: import std.stdio; struct S{ int opCall(int x){ return x+1; } } S s; void main(){ auto x = 1; writeln(x.s); }
That is _very_ broken IMHO. It makes no sense for parens to be optional with opCall. The whole point of opCall is to overload the parens!
 The closest that you could get would be a factory function, which is
 quite different. But constructors themselves cannot be generic. As such,
 using constructors with UFCS in generic code just doesn't work, meaning
 that the only gain that you're getting from using UFCS with constructors
 is that you get a slightly different syntax that you might like better
 for one reason or another. But I see no technical reason why it could add
 any benefit over simply calling the constructor normally. And as such, I
 think that allowing UFCS to work with constructors is definitely an
 anti-feature.
 ...
To be an anti-feature it has to be harmful, not just of less benefit than some other (aspect of the) feature.
I _do_ think that it's harmful. It obfuscates code without adding any benefit. - Jonathan M Davis
Jul 02 2013
next sibling parent reply "John Colvin" <john.loughran.colvin gmail.com> writes:
On Wednesday, 3 July 2013 at 03:22:16 UTC, Jonathan M Davis wrote:
 That is _very_ broken IMHO. It makes no sense for parens to be 
 optional with
 opCall. The whole point of opCall is to overload the parens!
So much about optional parenthesis is broken. I really wish things weren't going that way, it obfusticates the difference between a callable and the result in a really nasty way, and it doesn't work for function pointers (nor does UFCS unfortunately).
Jul 03 2013
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 07/03/2013 04:53 PM, John Colvin wrote:
 On Wednesday, 3 July 2013 at 03:22:16 UTC, Jonathan M Davis wrote:
 That is _very_ broken IMHO. It makes no sense for parens to be
 optional with
 opCall. The whole point of opCall is to overload the parens!
So much about optional parenthesis is broken. I really wish things weren't going that way, it obfusticates the difference between a callable and the result in a really nasty way, and it doesn't work for function pointers (nor does UFCS unfortunately).
Yes, UFCS works.
Jul 03 2013
parent reply "John Colvin" <john.loughran.colvin gmail.com> writes:
On Wednesday, 3 July 2013 at 15:05:29 UTC, Timon Gehr wrote:
 On 07/03/2013 04:53 PM, John Colvin wrote:
 On Wednesday, 3 July 2013 at 03:22:16 UTC, Jonathan M Davis 
 wrote:
 That is _very_ broken IMHO. It makes no sense for parens to be
 optional with
 opCall. The whole point of opCall is to overload the parens!
So much about optional parenthesis is broken. I really wish things weren't going that way, it obfusticates the difference between a callable and the result in a really nasty way, and it doesn't work for function pointers (nor does UFCS unfortunately).
Yes, UFCS works.
struct A {} void foo(A a) {} void main() { A a; auto foo_ptr = &foo; a.foo_ptr(); //Error: undefined identifier 'foo_ptr' a.foo_ptr; //Error: no property 'foo_ptr' for type 'A' }
Jul 03 2013
parent Artur Skawina <art.08.09 gmail.com> writes:
On 07/03/13 17:11, John Colvin wrote:
 On Wednesday, 3 July 2013 at 15:05:29 UTC, Timon Gehr wrote:
 On 07/03/2013 04:53 PM, John Colvin wrote:
 On Wednesday, 3 July 2013 at 03:22:16 UTC, Jonathan M Davis wrote:
 That is _very_ broken IMHO. It makes no sense for parens to be
 optional with
 opCall. The whole point of opCall is to overload the parens!
So much about optional parenthesis is broken. I really wish things weren't going that way, it obfusticates the difference between a callable and the result in a really nasty way, and it doesn't work for function pointers (nor does UFCS unfortunately).
Yes, UFCS works.
struct A {} void foo(A a) {} void main() { A a; auto foo_ptr = &foo; a.foo_ptr(); //Error: undefined identifier 'foo_ptr' a.foo_ptr; //Error: no property 'foo_ptr' for type 'A' }
Move the "auto foo_ptr = &foo;" line outside of `main` and it'll work. UFCS has several problems that need to be fixed and this is just one of them, but it isn't function-pointer specific. artur
Jul 03 2013
prev sibling parent "monarch_dodra" <monarchdodra gmail.com> writes:
On Wednesday, 3 July 2013 at 14:53:56 UTC, John Colvin wrote:
 On Wednesday, 3 July 2013 at 03:22:16 UTC, Jonathan M Davis 
 wrote:
 That is _very_ broken IMHO. It makes no sense for parens to be 
 optional with
 opCall. The whole point of opCall is to overload the parens!
So much about optional parenthesis is broken. I really wish things weren't going that way, it obfusticates the difference between a callable and the result in a really nasty way, and it doesn't work for function pointers (nor does UFCS unfortunately).
While I agree with you, I think that discussion is passed. I don't think anybody wants to resurect it (IMO).
Jul 03 2013
prev sibling parent "monarch_dodra" <monarchdodra gmail.com> writes:
On Wednesday, 3 July 2013 at 03:22:16 UTC, Jonathan M Davis wrote:
 On Wednesday, July 03, 2013 04:54:41 Timon Gehr wrote:
 On 07/03/2013 04:12 AM, Jonathan M Davis wrote:
 On Wednesday, July 03, 2013 04:00:45 bearophile wrote:
 deadalnix:
 The whole point of UFCS is to be able to provide additional
 custom "methods" to a object (class or struct). Constructor
 UFCS don't fulfill that use case.
 
 Nothing is removed from the language as factories method 
 can be
 introduced anyway.
This frames the topic in a wrong way. Constructors are not normal functions, they are special, but functional languages show us that's it's a very good idea to see them as functions. And the original point of UFCS doesn't matter much. What matters is what are the practical disadvantages of allowing UFCSyntax for constructors (like the original post in this thread), and what are their practical advantages/uses (like a handy usage in UFCS chains). Then we take a look at what's the resulting balance and we decide. And such decisions should then become the written specifics of this part of the D design.
The primary benefit of UFCS is generic code, because if uses UFCS, generic code doesn't have to care whether a function is a member function or a free function (particularly when that can vary drastically depending on what type is used to instantiate the template).
No. Generic code has to be careful with UFCS, because every method that is called on a suitable variable via UFCS can be (accidentally) replaced by the client code.
That's the whole point! If you absolutely have to be certain that it's not calling a free function, then don't use UFCS, but the primary benefits of UFCS   are making it so that generic code doesn't have to care whether a function is a free function and making it so that types can overload the behavior of free functions (e.g. having a member function find which is optimized for that type and can be used in place of std.algorithm.find).
 Construction is not and cannot be generic.
The point is that struct constructors can be generically used like other callables. import std.stdio, std.algorithm; struct S{ int x; } void main(){ auto x = [1,2,3]; writeln(x.map!S); } There is nothing to be gained from subtly breaking this analogy. UFCS can be applied to any callable. You are probably not going to like this, but the following code also works: import std.stdio; struct S{ int opCall(int x){ return x+1; } } S s; void main(){ auto x = 1; writeln(x.s); }
That is _very_ broken IMHO. It makes no sense for parens to be optional with opCall. The whole point of opCall is to overload the parens! - Jonathan M Davis
In a UFCS chain, it would make sense for the so-called "function object": struct Incrementor { int n; ref int opCall(ref int i) { return i+=n; } } int i, j; auto incrementByTwo = Incremementor(2); auto incrementByThree = Incremementor(3); i.incrementByTwo; j.incrementByThree; Here, the "function object" is treated just like a function (it is *designed* to behave like a function), so it should benefit from the same optional parens as a normal function. However, yeah, I think a standalone arg-less opCall with no parens is stupid (unless someone has a usecase for it?): It's an alias this in disguise, nothing more.
Jul 03 2013
prev sibling parent Artur Skawina <art.08.09 gmail.com> writes:
On 07/03/13 05:21, Jonathan M Davis wrote:
 On Wednesday, July 03, 2013 04:54:41 Timon Gehr wrote:
 There is nothing to be gained from subtly breaking this analogy. UFCS
 can be applied to any callable.

 You are probably not going to like this, but the following code also works:

 import std.stdio;

 struct S{
 int opCall(int x){ return x+1; }
 }

 S s;

 void main(){
 auto x = 1;
 writeln(x.s);
 }
That is _very_ broken IMHO. It makes no sense for parens to be optional with opCall. The whole point of opCall is to overload the parens!
This is an optional-parens issue, not an UFCS issue. Ie Timon's example only works w/o property enforcement, what actually happens is "writeln(x.s())". A really bad idea - yes, but it's not an UFCS and ctor specific problem. artur
Jul 03 2013
prev sibling parent reply "deadalnix" <deadalnix gmail.com> writes:
On Tuesday, 2 July 2013 at 19:47:07 UTC, Timon Gehr wrote:
 It is an artificial limitation, because you need to add an 
 explicit check after symbol lookup to ban constructors.
That seems very implementation defined. The lookup will gives you a class or struct declaration, so special casing the constructor is mandatory anyway.
Jul 02 2013
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 07/03/2013 03:37 AM, deadalnix wrote:
 On Tuesday, 2 July 2013 at 19:47:07 UTC, Timon Gehr wrote:
 It is an artificial limitation, because you need to add an explicit
 check after symbol lookup to ban constructors.
That seems very implementation defined.
It is true for two implementations and one spec I am aware of.
 The lookup will gives you a class or struct declaration,
Hopefully the UFCS lookup gives me a fully analysed call expression. That's the point of specifying features by rewrite rules.
 so special casing the constructor is mandatory anyway.
Yes, the call expression needs some kind of check in order to support struct literals. Cancelling that support in some way in case the call expression happens to have been generated by an UFCS rewrite requires another check, which complicates the formal specification of UFCS for no gain.
Jul 02 2013
prev sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 07/02/2013 09:35 AM, monarch_dodra wrote:
 ...

 One of the "big" problems with allowing UFCS and constructors is that a
 "." which meant "scope" can now mean "function call".
 ...
I missed this point. 'One of the drawbacks of UFCS is that "bar.foo" which meant "scope lookup" or "opDispatch instantiation" or "alias this lookup" can now also mean "UFCS lookup".' Fixed.
Jul 02 2013
parent reply "deadalnix" <deadalnix gmail.com> writes:
On Tuesday, 2 July 2013 at 12:57:50 UTC, Timon Gehr wrote:
 On 07/02/2013 09:35 AM, monarch_dodra wrote:
 ...

 One of the "big" problems with allowing UFCS and constructors 
 is that a
 "." which meant "scope" can now mean "function call".
 ...
I missed this point. 'One of the drawbacks of UFCS is that "bar.foo" which meant "scope lookup" or "opDispatch instantiation" or "alias this lookup" can now also mean "UFCS lookup".' Fixed.
That is an issue, we have all of this, and prioritization mechanism is implementation defined right now.
Jul 02 2013
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 07/02/2013 03:00 PM, deadalnix wrote:
 On Tuesday, 2 July 2013 at 12:57:50 UTC, Timon Gehr wrote:
 On 07/02/2013 09:35 AM, monarch_dodra wrote:
 ...

 One of the "big" problems with allowing UFCS and constructors is that a
 "." which meant "scope" can now mean "function call".
 ...
I missed this point. 'One of the drawbacks of UFCS is that "bar.foo" which meant "scope lookup" or "opDispatch instantiation" or "alias this lookup" can now also mean "UFCS lookup".' Fixed.
That is an issue, we have all of this, and prioritization mechanism is implementation defined right now.
Yup. I'll go with: 1: If member is present (current scope or super class scope), use that, otherwise try opDispatch in the same scopes. Block alias this at this point. 2: If no success, loop through all alias this definitions and apply 1. for the corresponding scopes. Collect possible members. 3: Error out if multiple possible distinct members. 4: Try UFCS rewrite if no matches. (Of course, even checking whether a member is present, or looping over all alias this definitions are not entirely trivial processes themselves in the general case.) This seems to be compatible with what DMD does.
Jul 02 2013