www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Is it possible to handle 'magic' property assignments a'la PHP?

reply "Gary Willoughby" <dev nomad.so> writes:
In D is it possible to handle accessing class properties that 
don't exist, similar to PHP's magic methods?

http://www.php.net/manual/en/language.oop5.overloading.php#object.set

For example, the below class doesn't have the 'x' property:

class T
{
     this()
     {
         this.x = "hello world!";
     }
}

Can i handle this in a programmatic way to intercept the property 
and deal with it?

Something like this:

class T
{
     this()
     {
         this.x = "hello world!";
     }

     opMagic(A, B)(A property, B value)
     {
         // property = "x", value = "hello world!"
     }
}
Jan 04 2014
parent reply "Gary Willoughby" <dev nomad.so> writes:
On Saturday, 4 January 2014 at 19:08:45 UTC, Gary Willoughby 
wrote:
 In D is it possible to handle accessing class properties that 
 don't exist, similar to PHP's magic methods?

 http://www.php.net/manual/en/language.oop5.overloading.php#object.set

 For example, the below class doesn't have the 'x' property:

 class T
 {
     this()
     {
         this.x = "hello world!";
     }
 }

 Can i handle this in a programmatic way to intercept the 
 property and deal with it?

 Something like this:

 class T
 {
     this()
     {
         this.x = "hello world!";
     }

     opMagic(A, B)(A property, B value)
     {
         // property = "x", value = "hello world!"
     }
 }
OMG i've just found opDispatch! http://dlang.org/operatoroverloading.html#Dispatch Fantastic!
Jan 04 2014
parent reply "Gary Willoughby" <dev nomad.so> writes:
On Saturday, 4 January 2014 at 19:17:31 UTC, Gary Willoughby 
wrote:
 OMG i've just found opDispatch!

 http://dlang.org/operatoroverloading.html#Dispatch

 Fantastic!
How to handle returning a value from a non-existent property?
Jan 04 2014
parent reply "Gary Willoughby" <dev nomad.so> writes:
On Saturday, 4 January 2014 at 19:19:43 UTC, Gary Willoughby 
wrote:
 On Saturday, 4 January 2014 at 19:17:31 UTC, Gary Willoughby 
 wrote:
 OMG i've just found opDispatch!

 http://dlang.org/operatoroverloading.html#Dispatch

 Fantastic!
How to handle returning a value from a non-existent property?
Got it! public void opDispatch(string s)(string value) { // handle. } public string opDispatch(string s)() { return "value"; }
Jan 04 2014
parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
On Saturday, 4 January 2014 at 19:26:50 UTC, Gary Willoughby 
wrote:
 Got it!
opDispatch rox, and there's all kinds of crazy stuff you can do with it. In my dom.d, I used it for three things: https://github.com/adamdruppe/misc-stuff-including-D-programming-language-web-stuff/blob/master/dom.d 1) easy access to attributes (node.href = "dlang.org"; and auto href = node.href;) using the technique you just showed. 2) the style stuff, which does a little rewriting: node.style.marginLeft = "10px"; -> adds "margin-left: 10px;" to the style attribute 3) Forwarding collections: document[".cool"].addClass("cool2"); uses opDispatch to forward any method to the collection without me writing much boilerplate code. This is also nice for calling external dynamic apis, you can use opDispatch to forward over a network call or something like that. I also used it in my jsvar.d file to make a var type in D, very similar to in Javascript: https://github.com/adamdruppe/misc-stuff-including-D-programming-language-web-stuff/blob/master/jsvar.d var a = var.emptyObject; a.cool = { writeln("hello world!"); } a.cool()(); // double parens needed due to broken property a.bar = 10; writeln(a.bar); // works and so on. D rox so much.
Jan 04 2014
next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Sat, Jan 04, 2014 at 07:52:11PM +0000, Adam D. Ruppe wrote:
 On Saturday, 4 January 2014 at 19:26:50 UTC, Gary Willoughby wrote:
Got it!
opDispatch rox, and there's all kinds of crazy stuff you can do with it. In my dom.d, I used it for three things:
[...] Somebody has also used opDispatch to do vector swizzling, such that: auto v = vec.wxzy; is equivalent to: auto v = [vec[0], vec[1], vec[2], vec[1]]; It's also possible to implement Roman numerals with opDispatch that doesn't require a string literal: Roman.II == 2 Roman.VII == 7 ... etc. by parsing the identifier passed to opDispatch at compile-time. Of course, these are arguably clever hacks than true, properly-motivated examples, but still, they exemplify what Andrei meant when he said that the power of opDispatch is largely still unexplored territory. T -- It's amazing how careful choice of punctuation can leave you hanging:
Jan 04 2014
prev sibling parent reply Philippe Sigaud <philippe.sigaud gmail.com> writes:
On Sat, Jan 4, 2014 at 10:08 PM, H. S. Teoh <hsteoh quickfur.ath.cx> wrote:

 Of course, these are arguably clever hacks than true, properly-motivated
 examples, but still, they exemplify what Andrei meant when he said that
 the power of opDispatch is largely still unexplored territory.
As Adam showed, it's very nice to make some clean API (or DSL). Another example I like is generating queries: auto result = table.findByFirstName; If Table has a "FirstName" field, then opDispatch will catch any findByXXXXX and generate the related query. A bit like Activerecord for Ruby.
Jan 05 2014
next sibling parent reply "TheFlyingFiddle" <kurtyan student.chalmers.se> writes:
Another simple example that have helped me tremendously when 
debugging OpenGL calls. A simple dispatcher that checks 
glGetError after every call.

struct GL
{
     auto opDispatch(string name, Args...)(Args args)
     {
         enum glName = "gl" ~ name;
	mixin(format("
         static if(is(ReturnType!%1$s == void))
	{
	    %1$s(args);
	    checkGLError();
	}
	else
	{
	    auto r = %1$s(args);
	    checkGLError();
	    return r;
	 }", glName));

     }
}
GL gl;

I simply use gl.funcName instead of glFuncName. opDispatch rules!.
Jan 05 2014
parent reply Artur Skawina <art.08.09 gmail.com> writes:
On 01/05/14 15:36, TheFlyingFiddle wrote:
 Another simple example that have helped me tremendously when debugging OpenGL
calls. A simple dispatcher that checks glGetError after every call.
 
 struct GL
 {
     auto opDispatch(string name, Args...)(Args args)
     {
         enum glName = "gl" ~ name;
     mixin(format("
         static if(is(ReturnType!%1$s == void))
     {
         %1$s(args);
         checkGLError();
     }
     else
     {
         auto r = %1$s(args);
         checkGLError();
         return r;
      }", glName));
 
     }
 }
 GL gl;
 
 I simply use gl.funcName instead of glFuncName. opDispatch rules!.
While 'void' is not a first class type in D, there /is/ a special case for returning 'void' from functions - so all of the above can simply be written as: struct gl { static auto ref opDispatch(string name, Args...)(Args args) { scope (exit) checkGLError(); return mixin("gl"~name~"(args)"); } } artur
Jan 05 2014
parent "TheFlyingFiddle" <kurtyan student.chalmers.se> writes:
On Sunday, 5 January 2014 at 17:17:27 UTC, Artur Skawina wrote:
 While 'void' is not a first class type in D, there /is/ a 
 special
 case for returning 'void' from functions - so all of the above 
 can
 simply be written as:

    struct gl {
       static auto ref opDispatch(string name, Args...)(Args 
 args) {
          scope (exit) checkGLError();
          return mixin("gl"~name~"(args)");
       }
    }

 artur
That's awesome! Thanks for enlightening me!
Jan 05 2014
prev sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2014-01-05 14:09, Philippe Sigaud wrote:

 As Adam showed, it's very nice to make some clean API (or DSL).

 Another example I like is generating queries:

 auto result = table.findByFirstName;

 If Table has a "FirstName" field, then opDispatch will catch any
 findByXXXXX and generate the related query. A bit like Activerecord
 for Ruby.
Just for the record. In Rails, that's the old, now discourage, Rails 2 syntax. In Rails 3 and later the following syntax is preferred: Table.where(first_name: "foo").first Which in D would look like: Table.where(["first_name": "foo"]).first; -- /Jacob Carlborg
Jan 05 2014
parent reply Philippe Sigaud <philippe.sigaud gmail.com> writes:
On Sun, Jan 5, 2014 at 5:18 PM, Jacob Carlborg <doob me.com> wrote:

 Just for the record. In Rails, that's the old, now discourage, Rails 2
 syntax.
I didn't know that, thanks. I read it during the holidays in Martin Fowler's book on DSL, but indeed that book is from 2005, IIRC.
 In Rails 3 and later the following syntax is preferred:

 Table.where(first_name: "foo").first

 Which in D would look like:

 Table.where(["first_name": "foo"]).first;
Yes, using AA is a nice idea, which avoids introducing first_name in the current scope. Some other possibilities could be: Table.where!(e => e.first_name == "foo").first; // Similar to std.range.filter Table.where.first_name!(e => e == "foo").first; Table.where((string first_name) => first_name == "foo").first; // By extracting the parameter name The syntax is heavier than your example, but some nice logic can put into a closure. Some also advocate: Table.where.first_name.equals("foo").first; But I find it a bit too clunky.
Jan 05 2014
parent reply Jacob Carlborg <doob me.com> writes:
On 2014-01-05 22:44, Philippe Sigaud wrote:

 I didn't know that, thanks. I read it during the holidays in Martin
 Fowler's book on DSL, but indeed that book is from 2005, IIRC.
That's a bit old :). According to this site[1] Rails 1.0 was released in December 2005. Rails 4.0 was released in June 2013. I think there are two reasons why they deprecated that syntax: * They want to avoid method_missing (same as opDispatch in D) * They want to have the name of a column close to the value of that column. Example: Table.find_first_by_fist_name_and_last_name("foo", "bar") In the above example the values and the columns they belongs to are quite disconnected. The column names are far to the right and the values are far to the left. With the AA syntax you get the column name and its value closely connected. It's clear to see which value belongs to which column.
 Yes, using AA is a nice idea, which avoids introducing first_name in
 the current scope.
I have to admit that it looks a lot better in Ruby than in D.
 Some other possibilities could be:

 Table.where!(e => e.first_name == "foo").first; // Similar to std.range.filter
This is the one I like best and it's possible to have the same syntax in Ruby as well with a plugin, Squeel[2]. This allows to have more advanced quires containing "or" and negation. The problem is it's basically only equality that works with this syntax. You cannot overload !=, && or || in D. You could of course use method names like "equals", "and" and "or". In Squeel they "solved" it by overloading | and & to mean "or" and "and". That causes other problems with operator precedence, one needs to wrap everything in parentheses. In D we would need more finer grained control of operator overloading, like overload == separately from !=. Or, I come back to this all the time, AST macros.
 Table.where.first_name!(e => e == "foo").first;
If I look at the API in Rails I think this could cause some conflicts with all the methods that are available on the object returned by Table.where.
 Table.where((string first_name) => first_name == "foo").first; // By
 extracting the parameter name

 The syntax is heavier than your example, but some nice logic can put
 into a closure.
Same problem as above.
 Some also advocate:

 Table.where.first_name.equals("foo").first;

 But I find it a bit too clunky.
Same problem as above. [1] http://railsapps.github.io/rails-release-history.html [2] https://github.com/activerecord-hackery/squeel -- /Jacob Carlborg
Jan 06 2014
parent reply Philippe Sigaud <philippe.sigaud gmail.com> writes:
 I didn't know that, thanks. I read it during the holidays in Martin
 Fowler's book on DSL, but indeed that book is from 2005, IIRC.
That's a bit old :). According to this site[1] Rails 1.0 was released in December 2005. Rails 4.0 was released in June 2013.
Ouch, that was 2010, my bad.
 I think there are two reasons why they deprecated that syntax:

 * They want to avoid method_missing (same as opDispatch in D)

 * They want to have the name of a column close to the value of that column.
 Example:

 Table.find_first_by_fist_name_and_last_name("foo", "bar")

 In the above example the values and the columns they belongs to are quite
 disconnected. The column names are far to the right and the values are far
 to the left. With the AA syntax you get the column name and its value
 closely connected. It's clear to see which value belongs to which column.
Yes indeed. Too clever for its own good.
 I have to admit that it looks a lot better in Ruby than in D.
Ruby does have a clean syntax (though I find blocks to be a bit heavy).
 Some other possibilities could be:

 Table.where!(e => e.first_name == "foo").first; // Similar to
 std.range.filter
This is the one I like best and it's possible to have the same syntax in Ruby as well with a plugin, Squeel[2]. This allows to have more advanced quires containing "or" and negation. The problem is it's basically only equality that works with this syntax. You cannot overload !=, && or || in D. You could of course use method names like "equals", "and" and "or".
But I'm not overloading any syntax here. I'm using a closure, not an expression template. Table.where!(e => e.first_name != "foo" && e.first_name.length > 4).first;
Jan 06 2014
parent reply Jacob Carlborg <doob me.com> writes:
On 2014-01-06 22:07, Philippe Sigaud wrote:

 Ruby does have a clean syntax (though I find blocks to be a bit heavy).
I like the block syntax. It allows one to create what looks like new statements: loop do end I would like to have that in D as well, but with braces instead: void loop (void delegate () dg); loop { // endless loop } In Ruby 1.9 they introduced a slight more lightweight syntax for lambdas: callback = -> { p "foo" } bar(callback)
 But I'm not overloading any syntax here. I'm using a closure, not an
 expression template.

 Table.where!(e => e.first_name != "foo" && e.first_name.length > 4).first;
How is that being transformed in to SQL then? -- /Jacob Carlborg
Jan 06 2014
parent reply Philippe Sigaud <philippe.sigaud gmail.com> writes:
On Tue, Jan 7, 2014 at 8:50 AM, Jacob Carlborg <doob me.com> wrote:
 I would like to have that in D as well, but with braces instead:

 void loop (void delegate () dg);

 loop {
     // endless loop
 }
What about: void loop(void delegate() dg); loop({ ... }); Since any block is a void delegate().
Jan 07 2014
parent reply Jacob Carlborg <doob me.com> writes:
On 2014-01-07 13:22, Philippe Sigaud wrote:

 What about:

 void loop(void delegate() dg);

 loop({
 ...

 });

 Since any block is a void delegate().
That's what we have now, and that doesn't look like a built-in statement ;) -- /Jacob Carlborg
Jan 07 2014
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Tue, Jan 07, 2014 at 03:35:43PM +0100, Jacob Carlborg wrote:
 On 2014-01-07 13:22, Philippe Sigaud wrote:
 
What about:

void loop(void delegate() dg);

loop({
...

});

Since any block is a void delegate().
That's what we have now, and that doesn't look like a built-in statement ;)
[...] Y'know, I've always wanted "trailing delegate syntax": func(x, y, z; p, q, r) { // body } gets translated into: func(p, q, r, (x, y, z) => /* body */); Since we already have UFCS, which translates a leading fragment into the first argument (x.func(y) --> func(x,y)), it seems perfectly reasonable to do something with the final argument too, like the above. This would allow one to implement, for example, foreach_reverse as a library function instead of a language keyword: void foreach_reverse(I, R)(R range, void delegate(I) dg) { ... dg(idx); ... } // Gets translated to: // foreach_reverse(range, (uint i) => /* body */); foreach_reverse (uint i; range) { ... // body } // And you can use UFCS too: range.foreach_reverse(uint i) { ... // body } I'm not holding my breath on this one, though. It's a rather big change and ultimately is just syntactic sugar. Maybe it can go on the list of features for D3... ;-) T -- Famous last words: I wonder what will happen if I do *this*...
Jan 07 2014
parent reply Jacob Carlborg <doob me.com> writes:
On 2014-01-07 16:58, H. S. Teoh wrote:

 Y'know, I've always wanted "trailing delegate syntax":

 	func(x, y, z; p, q, r) {
 		// body
 	}

 gets translated into:

 	func(p, q, r, (x, y, z) => /* body */);

 Since we already have UFCS, which translates a leading fragment into the
 first argument (x.func(y) --> func(x,y)), it seems perfectly reasonable
 to do something with the final argument too, like the above.

 This would allow one to implement, for example, foreach_reverse as a
 library function instead of a language keyword:

 	void foreach_reverse(I, R)(R range, void delegate(I) dg)
 	{
 		...
 		dg(idx);
 		...
 	}

 	// Gets translated to:
 	//	foreach_reverse(range, (uint i) => /* body */);
 	foreach_reverse (uint i; range) {
 		... // body
 	}

 	// And you can use UFCS too:
 	range.foreach_reverse(uint i) {
 		... // body
 	}
Exactly, that's what it is for. Perhaps supporting an alias parameter would be good as well, since those are inlined: void foo (alias dg) (); foo { // body } Translated to: foo!({ // body });
 I'm not holding my breath on this one, though. It's a rather big change
 and ultimately is just syntactic sugar. Maybe it can go on the list of
 features for D3... ;-)
I've brought this up before. If I recall correctly, it didn't was that much resistance as one could think. Although this was before we had the lambda syntax. -- /Jacob Carlborg
Jan 07 2014
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Tue, Jan 07, 2014 at 09:18:48PM +0100, Jacob Carlborg wrote:
 On 2014-01-07 16:58, H. S. Teoh wrote:
 
Y'know, I've always wanted "trailing delegate syntax":

	func(x, y, z; p, q, r) {
		// body
	}

gets translated into:

	func(p, q, r, (x, y, z) => /* body */);

Since we already have UFCS, which translates a leading fragment into
the first argument (x.func(y) --> func(x,y)), it seems perfectly
reasonable to do something with the final argument too, like the
above.

This would allow one to implement, for example, foreach_reverse as a
library function instead of a language keyword:

	void foreach_reverse(I, R)(R range, void delegate(I) dg)
	{
		...
		dg(idx);
		...
	}

	// Gets translated to:
	//	foreach_reverse(range, (uint i) => /* body */);
	foreach_reverse (uint i; range) {
		... // body
	}

	// And you can use UFCS too:
	range.foreach_reverse(uint i) {
		... // body
	}
Exactly, that's what it is for. Perhaps supporting an alias parameter would be good as well, since those are inlined: void foo (alias dg) (); foo { // body } Translated to: foo!({ // body });
I'm not holding my breath on this one, though. It's a rather big
change and ultimately is just syntactic sugar. Maybe it can go on the
list of features for D3... ;-)
I've brought this up before. If I recall correctly, it didn't was that much resistance as one could think. Although this was before we had the lambda syntax.
[...] If you have a good motivating use case in favor of this addition that can be used in a DIP, I'd vote for it. I like the alias idea, so here's the revised proposal: 1) Argumentless trailing-delegate syntax: // Given this declaration: void foo(alias dg)(); // We can write this: foo { // body } // which will get translated into: foo!({ /* body */ }); 2) With arguments: // Given this declaration: void foo(alias dg, A...)(A args); // Or its non-template equivalent: void foo(alias dg)(A arg1, B arg2, C arg3, ...); // We can write this: foo(a,b,c,...) { // body } // which gets translated into: foo!({ /* body */})(a,b,c,...); 3) With indexing arguments: // Given this declaration: void foo(alias dg, I..., A...)(A args) if (is(typeof(dg(I)))); // Or its non-template equivalent: void foo(alias dg)(A arg1, B arg2, C arg3, ...) { ... dg(i, j, k); ... } // We can write this: foo(i,j,k,... ; a,b,c,...) { // body } // which gets translated into: foo!((i,j,k,...) { /* body */ })(a,b,c,...); EXAMPLE: void for_every_other(alias loopBody, R)(R range) if (is(typeof(loopBody(ElementType!R.init)))) { while (!range.empty) { loopBody(range.front); range.popFront(); if (!range.empty) range.popFront(); } } // Prints: // --- // 1 // 3 // 5 // --- for_every_other (i; [1,2,3,4,5,6]) { writeln(i); } EXTENDED EXAMPLE: void for_every_other(alias loopBody, R)(R range) if (is(typeof(loopBody(size_t.init, ElementType!R.init)))) { size_t i=0; while (!range.empty) { loopBody(i, range.front); range.popFront(); if (!range.empty) { range.popFront(); i += 2; } } } // Prints: // --- // 0: "a" // 2: "c" // 4: "e" // --- for_every_other (i, j; ["a", "b", "c", "d", "e", "f"]) { writefln("%s: %s", i, j); } T -- Error: Keyboard not attached. Press F1 to continue. -- Yoon Ha Lee, CONLANG
Jan 07 2014
parent reply Jacob Carlborg <doob me.com> writes:
On 2014-01-07 21:44, H. S. Teoh wrote:

 If you have a good motivating use case in favor of this addition that
 can be used in a DIP, I'd vote for it.
I'm usually not good at these arguments. I mean, it would be nice to have but I don't have any strong arguments for it. It's just syntax sugar.
 I like the alias idea, so here's the revised proposal:

 1) Argumentless trailing-delegate syntax:

 	// Given this declaration:
 	void foo(alias dg)();

 	// We can write this:
 	foo {
 		// body
 	}

 	// which will get translated into:
 	foo!({ /* body */ });

 2) With arguments:

 	// Given this declaration:
 	void foo(alias dg, A...)(A args);

 	// Or its non-template equivalent:
 	void foo(alias dg)(A arg1, B arg2, C arg3, ...);

 	// We can write this:
 	foo(a,b,c,...) {
 		// body
 	}

 	// which gets translated into:
 	foo!({ /* body */})(a,b,c,...);

 3) With indexing arguments:

 	// Given this declaration:
 	void foo(alias dg, I..., A...)(A args)
 		if (is(typeof(dg(I))));

 	// Or its non-template equivalent:
 	void foo(alias dg)(A arg1, B arg2, C arg3, ...) {
 		...
 		dg(i, j, k);
 		...
 	}

 	// We can write this:
 	foo(i,j,k,... ; a,b,c,...) {
 		// body
 	}
I would prefer to have the delegate arguments last.
 	// which gets translated into:
 	foo!((i,j,k,...) { /* body */ })(a,b,c,...);


 EXAMPLE:

 	void for_every_other(alias loopBody, R)(R range)
 		if (is(typeof(loopBody(ElementType!R.init))))
 	{
 		while (!range.empty) {
 			loopBody(range.front);
 			range.popFront();
 			if (!range.empty)
 				range.popFront();
 		}
 	}

 	// Prints:
 	// ---
 	// 1
 	// 3
 	// 5
 	// ---
 	for_every_other (i; [1,2,3,4,5,6]) {
 		writeln(i);
 	}
If we instead have the delegate argument last UFCS still works: [1,2,3,4,5,6].for_every_other(i) { writeln(i); } Hmm. Actually, your example is more D like. I don't know which I example I like best. I'll see if I can write something down. -- /Jacob Carlborg
Jan 07 2014
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, Jan 08, 2014 at 08:32:15AM +0100, Jacob Carlborg wrote:
 On 2014-01-07 21:44, H. S. Teoh wrote:
[...]
I like the alias idea, so here's the revised proposal:

1) Argumentless trailing-delegate syntax:

	// Given this declaration:
	void foo(alias dg)();

	// We can write this:
	foo {
		// body
	}

	// which will get translated into:
	foo!({ /* body */ });

2) With arguments:

	// Given this declaration:
	void foo(alias dg, A...)(A args);

	// Or its non-template equivalent:
	void foo(alias dg)(A arg1, B arg2, C arg3, ...);

	// We can write this:
	foo(a,b,c,...) {
		// body
	}

	// which gets translated into:
	foo!({ /* body */})(a,b,c,...);

3) With indexing arguments:

	// Given this declaration:
	void foo(alias dg, I..., A...)(A args)
		if (is(typeof(dg(I))));

	// Or its non-template equivalent:
	void foo(alias dg)(A arg1, B arg2, C arg3, ...) {
		...
		dg(i, j, k);
		...
	}

	// We can write this:
	foo(i,j,k,... ; a,b,c,...) {
		// body
	}
I would prefer to have the delegate arguments last.
The reason I wrote it this way is so that it parallels the foreach construction better: my_foreach (i; range) { ... } parallels: foreach (i; range) { ... } [...]
EXAMPLE:

	void for_every_other(alias loopBody, R)(R range)
		if (is(typeof(loopBody(ElementType!R.init))))
	{
		while (!range.empty) {
			loopBody(range.front);
			range.popFront();
			if (!range.empty)
				range.popFront();
		}
	}

	// Prints:
	// ---
	// 1
	// 3
	// 5
	// ---
	for_every_other (i; [1,2,3,4,5,6]) {
		writeln(i);
	}
If we instead have the delegate argument last UFCS still works: [1,2,3,4,5,6].for_every_other(i) { writeln(i); } Hmm. Actually, your example is more D like. I don't know which I example I like best.
[...] Keep in mind that the identifier list before the ';' is actually the delegate's parameter list, it's not passing anything in. They are placeholders for what the function will pass to the delegate. So: my_foreach (i,j ; range) { writeln(i + j); } actually means: my_foreach(range, (i,j) => writeln(i + j)); and my_foreach could be implemented something like this: void my_foreach(alias dg, R)(R range) if (is(typeof(dg(size_t.init, ElementType!R.init)))) { size_t idx = 0; while (!range.empty) { // N.B.: calls dg with i = idx, j = range.front dg(idx, range.front); range.popFront(); idx++; } } If we go by this, then UFCS should still work: range.my_foreach(i,j) { /* body */ } should be translated to: my_foreach(i, j ; range) { /* body */ } which in turn translates to: my_foreach!((i, j) { /* body */ })(range); In the first case, there is no ambiguity with `range.my_foreach(i,j);`, which should translate to `my_foreach(range,i,j);`, because the presence of the trailing code block without an intervening ';' makes it clear that the above is intended, rather than `my_foreach(range,i,j);`. In fact, we can already almost get the desired syntax in the current language: /* Current D already supports this: */ range.my_foreach!((i,j) { /* body */ }); which isn't that much different from the proposed syntactic sugar: range.my_foreach(i,j) { /* body */ } We're just saving on the '!', ';', and an extra pair of parentheses. I guess the only real advantage is that we get to imitate built-in foreach syntax. E.g., if we use the form with arguments but no indexing arguments, we can pretend to be an if-statement: // (Whatever "dynamic if" means...) void dynamic_if(alias dg)(bool cond) if (is(typeof(dg()))) { // Haha, we're just wrapping the built-in 'if' cuz we // can. if (cond) dg(); } int x; dynamic_if (x==0) { writeln("Boo yah!"); } Or if we use the argumentless form to implement custom block constructs: void pure_block(alias dg)() pure if (is(typeof(dg()))) { dg(); } void nothrow_block(alias dg)() nothrow if (is(typeof(dg()))) { dg(); } void safe_block(alias dg)() safe if (is(typeof(dg()))) { dg(); } void main() { pure_block { // Whoopie! now we acquired a construct for // marking blocks of code pure! } nothrow_block { // And we can have multiple such blocks in a // single function. } safe_block { // Now I'm just showing off. :P } } T -- Curiosity kills the cat. Moral: don't be the cat.
Jan 08 2014
parent reply Jacob Carlborg <doob me.com> writes:
On 2014-01-08 19:04, H. S. Teoh wrote:

 The reason I wrote it this way is so that it parallels the foreach
 construction better:

 	my_foreach (i; range) {
 		...
 	}

 parallels:

 	foreach (i; range) {
 		...
 	}
I guessed that.
 Keep in mind that the identifier list before the ';' is actually the
 delegate's parameter list, it's not passing anything in. They are
 placeholders for what the function will pass to the delegate. So:

 	my_foreach (i,j ; range) {
 		writeln(i + j);
 	}

 actually means:

 	my_foreach(range, (i,j) => writeln(i + j));
Yeah, I know.
 and my_foreach could be implemented something like this:

 	void my_foreach(alias dg, R)(R range)
 		if (is(typeof(dg(size_t.init, ElementType!R.init))))
 	{
 		size_t idx = 0;
 		while (!range.empty) {
 			// N.B.: calls dg with i = idx, j = range.front
 			dg(idx, range.front);

 			range.popFront();
 			idx++;
 		}
 	}


 If we go by this, then UFCS should still work:

 	range.my_foreach(i,j) { /* body */ }

 should be translated to:

 	my_foreach(i, j ; range) { /* body */ }

 which in turn translates to:

 	my_foreach!((i, j) { /* body */ })(range);

 In the first case, there is no ambiguity with `range.my_foreach(i,j);`,
 which should translate to `my_foreach(range,i,j);`, because the presence
 of the trailing code block without an intervening ';' makes it clear
 that the above is intended, rather than `my_foreach(range,i,j);`.
Didn't think of that.
 In fact, we can already almost get the desired syntax in the current
 language:

 	/* Current D already supports this: */
 	range.my_foreach!((i,j) {
 		/* body */
 	});
Almost ;)
 which isn't that much different from the proposed syntactic sugar:

 	range.my_foreach(i,j) {
 		/* body */
 	}

 We're just saving on the '!', ';', and an extra pair of parentheses.
It quickly get clumsy when you need to pass regular arguments to the function: void foo (alias dg) (int a, int b); foo!((i, j) { // body })(1, 2); Not pretty.
 I guess the only real advantage is that we get to imitate built-in
 foreach syntax. E.g., if we use the form with arguments but no indexing
 arguments, we can pretend to be an if-statement:

 	// (Whatever "dynamic if" means...)
 	void dynamic_if(alias dg)(bool cond)
 		if (is(typeof(dg())))
 	{
 		// Haha, we're just wrapping the built-in 'if' cuz we
 		// can.
 		if (cond) dg();
 	}

 	int x;
 	dynamic_if (x==0) {
 		writeln("Boo yah!");
 	}

 Or if we use the argumentless form to implement custom block constructs:
BTW, what do you think about not needing braces if the delegate body only contains a single statement, like with regular statements: dynamic_if (x==0) writeln("foo"); With the alias syntax, I'm wondering if the compiler will have any problem with that you can pass almost anything to an alias parameter and not just a delegate. -- /Jacob Carlborg
Jan 09 2014
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Thu, Jan 09, 2014 at 09:49:17AM +0100, Jacob Carlborg wrote:
 On 2014-01-08 19:04, H. S. Teoh wrote:
[...]
In fact, we can already almost get the desired syntax in the current
language:

	/* Current D already supports this: */
	range.my_foreach!((i,j) {
		/* body */
	});
Almost ;)
which isn't that much different from the proposed syntactic sugar:

	range.my_foreach(i,j) {
		/* body */
	}

We're just saving on the '!', ';', and an extra pair of parentheses.
It quickly get clumsy when you need to pass regular arguments to the function: void foo (alias dg) (int a, int b); foo!((i, j) { // body })(1, 2); Not pretty.
True. So this should be one of the motivating use cases for our DIP.
I guess the only real advantage is that we get to imitate built-in
foreach syntax. E.g., if we use the form with arguments but no
indexing arguments, we can pretend to be an if-statement:

	// (Whatever "dynamic if" means...)
	void dynamic_if(alias dg)(bool cond)
		if (is(typeof(dg())))
	{
		// Haha, we're just wrapping the built-in 'if' cuz we
		// can.
		if (cond) dg();
	}

	int x;
	dynamic_if (x==0) {
		writeln("Boo yah!");
	}

Or if we use the argumentless form to implement custom block constructs:
BTW, what do you think about not needing braces if the delegate body only contains a single statement, like with regular statements: dynamic_if (x==0) writeln("foo"); With the alias syntax, I'm wondering if the compiler will have any problem with that you can pass almost anything to an alias parameter and not just a delegate.
[...] I'm afraid that this might become ambiguous, for example: int* gun(...) {...} func (x==0) *gun(y); Does the second statement mean `func!(() => *gun(y))(x==0)`, or does it mean `func(x==0) * gun(y)`? While it's not hard to disambiguate this semantically, it means it's impossible to parse before you analyze it, which is probably a bad idea. T -- Nobody is perfect. I am Nobody. -- pepoluan, GKC forum
Jan 09 2014
parent Jacob Carlborg <doob me.com> writes:
On 2014-01-09 18:57, H. S. Teoh wrote:

 I'm afraid that this might become ambiguous, for example:

 	int* gun(...) {...}

 	func (x==0)
 		*gun(y);

 Does the second statement mean `func!(() => *gun(y))(x==0)`, or does it
 mean `func(x==0) * gun(y)`? While it's not hard to disambiguate this
 semantically, it means it's impossible to parse before you analyze it,
 which is probably a bad idea.
Right, probably not a good idea. -- /Jacob Carlborg
Jan 09 2014