August 22, 2013
On Thursday, 22 August 2013 at 14:29:50 UTC, Dicebot wrote:
> Wait, what? Alias parameters for templates have nothing to do with template argument lists (other than being one possible type of that list element). In other words, "T..." can contain more stuff than "alias T..." (imaginary syntax).
>

"other than being one possible type of that list element"

Indeed it is the only possible meaning and it the right one.

> And, considering the fact that we already have two different alias semantics (for template alias parameters and normal aliases), it is a dangerous terminology to chose anyway.

Yes, the term have 2 meanings. I simply reuse one of theses meaning without adding a new one.
August 22, 2013
On Thursday, 22 August 2013 at 14:32:27 UTC, Dicebot wrote:
> Well, it falls into the same issue "named after what you can do with it" vs "named after what it is". The very meaning of word "alias" suggests that calling something "alias for X" is just adding naming indirection on top of X ;)

That is what an alias parameter is, and I do think that regular alias and alias parameter should converge.
August 22, 2013
On Thu, Aug 22, 2013 at 01:19:53PM +0200, deadalnix wrote:
> So, I took some time to think about this. Let me propose something close, but different.
> 
> I'll try to define sequences more precisely and define some tools that allow to implement tuples in a nice way on top of it.
[...]

Originally, I typed up a long response to what you posted, but suddenly, it dawned on me that your idea, quoted below, is actually something much more general and applicable than tuples alone. So I decided to dedicate my reply to it:

> The missing piece is an auto dispatch function. I propose here a simple rewrite rule, as this is simple to implement and can be really effective.
> 
> auto (a, b) = foo();
> 
> is rewritten as
> 
> auto tmp = foo(); // Here tmp is a sequence of declaration of value.
> No need to create lvalues (especially is a complex copy is
> involved).
> assert(tmp.length == 2); // Should be removed anyway for sequences.
> auto a = tmp[0];
> auto b = tmp[1];
> 
> This allow to make any user type unpackable. Or even arrays, slices, randomAccessRanges, etc . . .

Now *this* is something new, and worth talking about.

Suppose we forget about the whole tuple fiasco, and forget that there's such a thing as a tuple (or TypeTuple or whatever else there is that's confusing everybody).  Just with this syntax alone, we can solve all kinds of problems:

- If f is a function that returns some kind of array, we can have
  automatic unpacking of arrays:

	int[] func() { return [1,2,3]; }

	void main() {
		auto (x, y, z) = func();
		// Equivalent to:
		// auto tmp = func();
		// x = tmp[0];
		// y = tmp[1];
		// z = tmp[2];
	}

- We can automatically unpack regex matches:

	import std.regex;
	auto (x, y, z) = inputString.match(`(\d+)\s+(\w+)\s+(\S+)`).captures;
	// x = string matched by (\d+)
	// y = string matched by (\w+)
	// z = string matched by (\S+)

- Like you said, any indexable range can be supported by this syntax. In
  fact, I'd argue that you should be able to do this even with just an
  input range:

	auto (x, y, z) = makeInputRange();

  should be translated into:

	auto tmp = makeInputRange();
	assert(!tmp.empty);
	auto x = tmp.front;
	tmp.popFront();
	assert(!tmp.empty);
	auto y = tmp.front;
	tmp.popFront();
	assert(!tmp.empty);
	auto z = tmp.front;

  Since ranges are a major selling feature of D, I'd argue that
  in-language support should be completely appropriate, and even
  desirable. (In fact, foreach already understands what a range is, so
  why not extend it here as well.)


You said that the missing piece was an auto dispatch function. Well, I think if we add yet another piece to it, this could become a killer feature in D, even regardless of what happens with the whole tuples issue:

The above is all nice and good, except that you can't pass a tuple/array/range return from a function into a poly-adic function's arguments. That is to say:

	int[] func1() { return [1,2,3]; }
	void func2(int x, int y, int z) { ... }

	// Currently this line doesn't compile:
	func2(func1());

I used int[] for illustration purposes only; it can also be, say, an input range of ints:

	T func1() { ... }
	assert(isInputRange!T && is(ElementType!T == int));

	void func2(int x, int y, int z) { ... }

	func2(func1());
	// ^^^ this will be rewritten into:
	// auto tmp = func1();
	// auto arg1 = tmp.front;
	// tmp.popFront();
	// auto arg2 = tmp.front;
	// tmp.popFront();
	// auto arg3 = tmp.front;
	// func2(arg1, arg2, arg3);

Of course, if T is an array, then it will be rewritten into func2(tmp[0], tmp[1], tmp[2]); same goes if T is a Tuple, etc.. Anything indexable with array notation should undergo this rewriting. So you could write:

	Tuple!(int,string,bool) func1() {
		return tuple(1, "a", true);
	}

	void func2(int x, string y, bool z) { ... }

	func2(func1());
	// ^^^^ this gets rewritten into:
	// auto tmp = func1();
	// func2(tmp[0], tmp[1], tmp[2]);

My point is that this auto-dispatch / auto-repack is not limited to tuples alone. It can be made to work in a nice way to anything that has array indexing notation or a range interface.

This would solve the problem of multiple return values, for example. You could have a div() function that returns a quotient and remainder:

	auto div(int x, int y) {
		...
		return [q, r];
		// Or, (q, r), or a 2-element input range
	}

	auto (x, y) = div(13, 7);


T

-- 
Computers aren't intelligent; they only think they are.
August 23, 2013
On Thursday, 22 August 2013 at 19:48:36 UTC, H. S. Teoh wrote:
> Now *this* is something new, and worth talking about.
>
> Suppose we forget about the whole tuple fiasco, and forget that there's
> such a thing as a tuple (or TypeTuple or whatever else there is that's
> confusing everybody).  Just with this syntax alone, we can solve all
> kinds of problems:
>
> - If f is a function that returns some kind of array, we can have
>   automatic unpacking of arrays:
>
> 	int[] func() { return [1,2,3]; }
>
> 	void main() {
> 		auto (x, y, z) = func();
> 		// Equivalent to:
> 		// auto tmp = func();
> 		// x = tmp[0];
> 		// y = tmp[1];
> 		// z = tmp[2];
> 	}
>
> - We can automatically unpack regex matches:
>
> 	import std.regex;
> 	auto (x, y, z) = inputString.match(`(\d+)\s+(\w+)\s+(\S+)`).captures;
> 	// x = string matched by (\d+)
> 	// y = string matched by (\w+)
> 	// z = string matched by (\S+)
>
> - Like you said, any indexable range can be supported by this syntax. In
>   fact, I'd argue that you should be able to do this even with just an
>   input range:
>
> 	auto (x, y, z) = makeInputRange();
>
>   should be translated into:
>
> 	auto tmp = makeInputRange();
> 	assert(!tmp.empty);
> 	auto x = tmp.front;
> 	tmp.popFront();
> 	assert(!tmp.empty);
> 	auto y = tmp.front;
> 	tmp.popFront();
> 	assert(!tmp.empty);
> 	auto z = tmp.front;
>
>   Since ranges are a major selling feature of D, I'd argue that
>   in-language support should be completely appropriate, and even
>   desirable. (In fact, foreach already understands what a range is, so
>   why not extend it here as well.)
>

Yes that is the intended effect.

>
> You said that the missing piece was an auto dispatch function. Well, I
> think if we add yet another piece to it, this could become a killer
> feature in D, even regardless of what happens with the whole tuples
> issue:
>
> The above is all nice and good, except that you can't pass a
> tuple/array/range return from a function into a poly-adic function's
> arguments. That is to say:
>
> 	int[] func1() { return [1,2,3]; }
> 	void func2(int x, int y, int z) { ... }
>
> 	// Currently this line doesn't compile:
> 	func2(func1());
>
> I used int[] for illustration purposes only; it can also be, say, an
> input range of ints:
>
> 	T func1() { ... }
> 	assert(isInputRange!T && is(ElementType!T == int));
>
> 	void func2(int x, int y, int z) { ... }
>
> 	func2(func1());
> 	// ^^^ this will be rewritten into:
> 	// auto tmp = func1();
> 	// auto arg1 = tmp.front;
> 	// tmp.popFront();
> 	// auto arg2 = tmp.front;
> 	// tmp.popFront();
> 	// auto arg3 = tmp.front;
> 	// func2(arg1, arg2, arg3);
>
> Of course, if T is an array, then it will be rewritten into
> func2(tmp[0], tmp[1], tmp[2]); same goes if T is a Tuple, etc.. Anything
> indexable with array notation should undergo this rewriting. So you
> could write:
>
> 	Tuple!(int,string,bool) func1() {
> 		return tuple(1, "a", true);
> 	}
>
> 	void func2(int x, string y, bool z) { ... }
>
> 	func2(func1());
> 	// ^^^^ this gets rewritten into:
> 	// auto tmp = func1();
> 	// func2(tmp[0], tmp[1], tmp[2]);
>
> My point is that this auto-dispatch / auto-repack is not limited to
> tuples alone. It can be made to work in a nice way to anything that has
> array indexing notation or a range interface.
>
> This would solve the problem of multiple return values, for example. You
> could have a div() function that returns a quotient and remainder:
>
> 	auto div(int x, int y) {
> 		...
> 		return [q, r];
> 		// Or, (q, r), or a 2-element input range
> 	}
>
> 	auto (x, y) = div(13, 7);
>
>
> T

I'd like to see that being allowed explicitly, not implicitly as you propose.

foo(int, int, int) must be different than foo(int[3]) . If not, the TypeTuple unpacking mess is taken to yet another level.

The good news is that this is implementable with the proposal :

int, int, int dispatch(int[3] args) {
    return args[0], args[1], args[2];
}

foo(dispatch(arr));

To sum up, the only major change my proposal got it the ability to return values sequences. That plus some syntaxic sugar provide everything we need.

I'm thinking about it for month now and it only poped recently. I'm now convinced that this is the way forward :
 - If ABI allow multiple return values, we can take advantage of it.
 - We can have clean tuples.
 - We give more expressiveness to user defined types.
August 23, 2013
H. S. Teoh:

> any indexable range can be supported by this syntax. In
>   fact, I'd argue that you should be able to do this even with just an
>   input range:
>
> 	auto (x, y, z) = makeInputRange();
>
>   should be translated into:
>
> 	auto tmp = makeInputRange();
> 	assert(!tmp.empty);
> 	auto x = tmp.front;
> 	tmp.popFront();
> 	assert(!tmp.empty);
> 	auto y = tmp.front;
> 	tmp.popFront();
> 	assert(!tmp.empty);
> 	auto z = tmp.front;
>
>   Since ranges are a major selling feature of D, I'd argue that
>   in-language support should be completely appropriate, and even
>   desirable.

Yes, this is an "obvious" nice feature to support, I didn't list it because I wasn't bold enough :-)

This is a good situation to show how other languages do, this is Python2:

>>> lazy = (x * x for x in xrange(1, 4))
>>> lazy
<generator object <genexpr> at 0x02230238>
>>> a, b, c = lazy
>>> a
1
>>> b
4
>>> c
9

Similar code is possible in Haskell and Perl6.

Bye,
bearophile
1 2 3
Next ›   Last »