Is it possible to handle 'magic' property assignments a'la PHP?

H. S. Teoh hsteoh at quickfur.ath.cx
Wed Jan 8 10:04:47 PST 2014


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.


More information about the Digitalmars-d-learn mailing list