www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Setter chaining

reply DigitalDesigns <DigitalDesigns gmail.com> writes:
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
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
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
parent reply DigitalDesigns <DigitalDesigns gmail.com> writes:
On Wednesday, 30 May 2018 at 15:46:36 UTC, Steven Schveighoffer 
wrote:
 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
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.
 auto 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
parent DigitalDesigns <DigitalDesigns gmail.com> writes:
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