January 08, 2014
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
January 08, 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.
January 09, 2014
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
January 09, 2014
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
January 09, 2014
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
1 2 3
Next ›   Last »