digitalmars.D.learn - Setter chaining
- DigitalDesigns (17/17) May 30 2018 Does it sound good?
- Steven Schveighoffer (12/35) May 30 2018 Yes, I do this kind of stuff, but you need to word your functions correc...
- DigitalDesigns (49/90) May 30 2018 Well, what I mean is to be able to have the ability to assign
- DigitalDesigns (47/47) May 30 2018 The above idea can be emulated in code, abiet ugly and useless:
Does it sound good? class X { double x; property X foo(double y) { x = y; return this; } property X bar(double y) { x = y + 5; return this; } } void main() { X x = new X(); x.foo(3).bar(4); } It sort of emulates UFCS but I'm not sure if it's more trouble than it's worth. I figure it would be better than just returning void as it provides the option to chain but not sure if it will come back to bite me.
May 30 2018
On 5/30/18 10:49 AM, DigitalDesigns wrote:Does it sound good? class X { double x; property X foo(double y) { x = y; return this; } property X bar(double y) { x = y + 5; return this; } } void main() { X x = new X(); x.foo(3).bar(4); } It sort of emulates UFCS but I'm not sure if it's more trouble than it's worth. I figure it would be better than just returning void as it provides the option to chain but not sure if it will come back to bite me.Yes, I do this kind of stuff, but you need to word your functions correctly. I would avoid property there, as this implies you should use it like: x.foo = 5; and if you return a reference to the type itself, it will read weird if you do it that way: auto five = (x.foo = 5); Here the name of the function is really really important. In my use case, I am kind of using it in an SQL builder type, where each time you call a method it adds some piece of the query. Like: auto query = table.select("id, name").where("foo = 5").orderBy("name"); -Steve
May 30 2018
On Wednesday, 30 May 2018 at 15:46:36 UTC, Steven Schveighoffer wrote:On 5/30/18 10:49 AM, DigitalDesigns wrote:Well, what I mean is to be able to have the ability to assign like a field or a function. sometimes I might want to use it as a property like x.foo = 5; and sometimes like a method x.foo(5).foo(8); for chaining. Rather than having to to create setfoo and such. Since D allows both syntaxes to be used, rather than returning void, turning parenting object allows the chaining to take place. Since the trick here is to be consistent, all setters must follow this principle, it shouldn't be a problem with consistency.Does it sound good? class X { double x; property X foo(double y) { x = y; return this; } property X bar(double y) { x = y + 5; return this; } } void main() { X x = new X(); x.foo(3).bar(4); } It sort of emulates UFCS but I'm not sure if it's more trouble than it's worth. I figure it would be better than just returning void as it provides the option to chain but not sure if it will come back to bite me.Yes, I do this kind of stuff, but you need to word your functions correctly. I would avoid property there, as this implies you should use it like: x.foo = 5; and if you return a reference to the type itself, it will read weird if you do it that way: auto five = (x.foo = 5); Here the name of the function is really really important. In my use case, I am kind of using it in an SQL builder type, where each time you call a method it adds some piece of the query. Like: auto query = table.select("id, name").where("foo = 5").orderBy("name"); -Steveauto five = (x.foo = 5);I wouldn't do that,just doesn't look right but I see your point. What would be cool is if D had some special way to return the object of the class the setter was in. auto x = ( x.foo = 5); Here returns x but first computes x.foo = 5;. In a way, it is like "this". gets the "this" of the function. Could work on any function: class Y { void foo(); property int baz(); property double bar(int x); } y. foo() returns y; y. baz() returns y; y. bar(3) returns y; Essentially one could few all functions as returning this and the value in a tuple and be default the value is returned and using a "selector" character will return this. class Y { Tuple!(void, typeof(this)) foo(); Tuple!(int, typeof(this)) baz(); Tuple!(double, typeof(this)) bar(int x); } y.foo()[1] returns y; ... The compiler could simplify all this and by putting this in a register it could be be quite fast. In fact, since these are member functions and this is passed it will just fall through so very little overhead. The compiler can also optimize the code. Might take a bit to verify all the corner cases but would probably be useful once people could use it and get used to it. , $, |, ?, ! or many symbols could be used without ambiguity because a dot will always preceded them.
May 30 2018
The above idea can be emulated in code, abiet ugly and useless: https://dpaste.dzfl.pl/bd118bc1910c import std.stdio; struct CT(A,B) { A v; B t; alias v this; B opUnary(string s)() if (s == "~") { return t; } A opUnary(string s)() if (s == "*") { return v; } } class C { int q; CT!(int, typeof(this)) foo() { q = 3; CT!(int, typeof(this)) v; v.t = this; v.v = q; return v; } CT!(int, typeof(this)) bar(int y) { q = q + y; CT!(int, typeof(this)) v; v.t = this; v.v = q; return v; } } void main() { C c = new C(); auto x = *((~c.foo()).bar(6)); writeln(x); } With a language implementation, all one would need is a symbol, everything would simplify to class C { int q; int foo() { q = 3; return q; } int bar(int y) { q = q + y; return q;} } auto x = c.#foo().bar(6);
May 30 2018