digitalmars.D - UFCS and constructors
- monarch_dodra (46/46) Jul 02 2013 Coming back from learn here. There was an example where somebody
- bearophile (8/10) Jul 02 2013 I think it's allowed, but such design decisions should be written
- Jonathan M Davis (6/13) Jul 02 2013 I'm not sure that it was ever decided one way or the other so much as ha...
- deadalnix (2/20) Jul 02 2013 We are 2. that is horrible.
- Kenji Hara (31/54) Jul 02 2013 I don't know what design decision had been there about it.
- Maxim Fomin (8/24) Jul 02 2013 Just for the interest - try to guess what following in D may be:
- Timon Gehr (15/25) Jul 02 2013 Aggregate initialization is the job of the constructor. It is a default
- monarch_dodra (10/19) Jul 02 2013 Yeah... tailored for confusion... that could be the biggest issue
- Jonathan M Davis (8/11) Jul 02 2013 There's nothing artificial about it. Constructors are not normal functio...
- Timon Gehr (13/25) Jul 02 2013 It is an artificial limitation, because you need to add an explicit
- Maxim Fomin (16/25) Jul 02 2013 class A
- Timon Gehr (19/49) Jul 02 2013 (We are discussing struct constructors.)
- deadalnix (6/10) Jul 02 2013 The whole point of UFCS is to be able to provide additional
- bearophile (13/18) Jul 02 2013 This frames the topic in a wrong way. Constructors are not normal
- Jonathan M Davis (14/33) Jul 02 2013 The primary benefit of UFCS is generic code, because if uses UFCS, gener...
- Timon Gehr (28/62) Jul 02 2013 No. Generic code has to be careful with UFCS, because every method that
- Jonathan M Davis (11/87) Jul 02 2013 That's the whole point! If you absolutely have to be certain that it's n...
- John Colvin (5/8) Jul 03 2013 So much about optional parenthesis is broken. I really wish
- Timon Gehr (2/10) Jul 03 2013 Yes, UFCS works.
- John Colvin (10/25) Jul 03 2013 struct A {}
- Artur Skawina (5/30) Jul 03 2013 Move the "auto foo_ptr = &foo;" line outside of `main` and it'll work.
- monarch_dodra (3/13) Jul 03 2013 While I agree with you, I think that discussion is passed. I
- monarch_dodra (22/117) Jul 03 2013 In a UFCS chain, it would make sense for the so-called "function
- Artur Skawina (5/26) Jul 03 2013 This is an optional-parens issue, not an UFCS issue. Ie Timon's example
- deadalnix (4/6) Jul 02 2013 That seems very implementation defined. The lookup will gives you
- Timon Gehr (9/16) Jul 02 2013 Hopefully the UFCS lookup gives me a fully analysed call expression.
- Timon Gehr (6/10) Jul 02 2013 I missed this point.
- deadalnix (3/15) Jul 02 2013 That is an issue, we have all of this, and prioritization
- Timon Gehr (13/30) Jul 02 2013 Yup. I'll go with:
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
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
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
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:We are 2. that is horrible.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
2013/7/2 deadalnix <deadalnix gmail.com>On Tuesday, 2 July 2013 at 08:16:38 UTC, Jonathan M Davis wrote: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 HaraOn Tuesday, July 02, 2013 09:35:38 monarch_dodra wrote:We are 2. that is horrible.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
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
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
On Tuesday, 2 July 2013 at 12:46:42 UTC, Timon Gehr wrote:On 07/02/2013 09:35 AM, monarch_dodra wrote: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.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); }
Jul 02 2013
On Tuesday, July 02, 2013 14:46:41 Timon Gehr wrote: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 DavisDo we want to keep it?There is no reason to artificially ban it.
Jul 02 2013
On 07/02/2013 07:58 PM, Jonathan M Davis wrote:On Tuesday, July 02, 2013 14:46:41 Timon Gehr wrote: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?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 DavisDo we want to keep it?There is no reason to artificially ban it.
Jul 02 2013
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
On 07/02/2013 10:16 PM, Maxim Fomin wrote:On Tuesday, 2 July 2013 at 19:47:07 UTC, Timon Gehr wrote:(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.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 logicLogics 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
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
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
On Wednesday, July 03, 2013 04:00:45 bearophile wrote:deadalnix: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 DavisThe 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.
Jul 02 2013
On 07/03/2013 04:12 AM, Jonathan M Davis wrote:On Wednesday, July 03, 2013 04:00:45 bearophile wrote: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.deadalnix: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).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.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
On Wednesday, July 03, 2013 04:54:41 Timon Gehr wrote:On 07/03/2013 04:12 AM, Jonathan M Davis wrote: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).On Wednesday, July 03, 2013 04:00:45 bearophile wrote: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.deadalnix: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).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.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!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); }I _do_ think that it's harmful. It obfuscates code without adding any benefit. - Jonathan M DavisThe 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
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
On 07/03/2013 04:53 PM, John Colvin wrote:On Wednesday, 3 July 2013 at 03:22:16 UTC, Jonathan M Davis wrote:Yes, UFCS works.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
On Wednesday, 3 July 2013 at 15:05:29 UTC, Timon Gehr wrote:On 07/03/2013 04:53 PM, John Colvin wrote: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' }On Wednesday, 3 July 2013 at 03:22:16 UTC, Jonathan M Davis wrote:Yes, UFCS works.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
On 07/03/13 17:11, John Colvin wrote:On Wednesday, 3 July 2013 at 15:05:29 UTC, Timon Gehr wrote: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. arturOn 07/03/2013 04:53 PM, John Colvin wrote: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' }On Wednesday, 3 July 2013 at 03:22:16 UTC, Jonathan M Davis wrote:Yes, UFCS works.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
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:While I agree with you, I think that discussion is passed. I don't think anybody wants to resurect it (IMO).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
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: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.On 07/03/2013 04:12 AM, Jonathan M Davis wrote: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).On Wednesday, July 03, 2013 04:00:45 bearophile wrote: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.deadalnix: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).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.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 DavisConstruction 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); }
Jul 03 2013
On 07/03/13 05:21, Jonathan M Davis wrote:On Wednesday, July 03, 2013 04:54:41 Timon Gehr wrote: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. arturThere 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!
Jul 03 2013
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
On 07/03/2013 03:37 AM, deadalnix wrote:On Tuesday, 2 July 2013 at 19:47:07 UTC, Timon Gehr wrote:It is true for two implementations and one spec I am aware of.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,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
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
On Tuesday, 2 July 2013 at 12:57:50 UTC, Timon Gehr wrote:On 07/02/2013 09:35 AM, monarch_dodra wrote:That is an issue, we have all of this, and prioritization mechanism is implementation defined right now.... 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
On 07/02/2013 03:00 PM, deadalnix wrote:On Tuesday, 2 July 2013 at 12:57:50 UTC, Timon Gehr wrote: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.On 07/02/2013 09:35 AM, monarch_dodra wrote:That is an issue, we have all of this, and prioritization mechanism is implementation defined right now.... 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