April 24, 2020 Re: I dun a DIP, possibly the best DIP ever | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter Bright | On Friday, 24 April 2020 at 08:35:53 UTC, Walter Bright wrote:
> On 4/24/2020 1:24 AM, Walter Bright wrote:
>
> Please keep in mind that the following works today:
>
> void foo(int);
>
> template tuple(T...) { enum tuple = T; }
>
> void test() {
> auto t = tuple!(1, 2, 3);
> static foreach (i, e; t)
> foo(e + i);
> }
>
> and generates:
>
> void test() {
> foo(1); foo(3); foo(5);
> }
not on 64bit.
on 64bit it's cannot pass argument cast(ulong)__t_field_0 + 0LU of type ulong to parameter int.
on 32 bit it genrates
(int, int, int) t = tuple(1, 2, 3);
foo(cast(ulong)__t_field_0 + 0LU);
foo(cast(ulong)__t_field_1 + 1LU);
foo(cast(ulong)__t_field_2 + 2LU);
|
April 24, 2020 Re: I dun a DIP, possibly the best DIP ever | ||||
---|---|---|---|---|
| ||||
Posted in reply to Stefan Koch | On Friday, 24 April 2020 at 08:52:41 UTC, Stefan Koch wrote:
> On Friday, 24 April 2020 at 08:35:53 UTC, Walter Bright wrote:
>> On 4/24/2020 1:24 AM, Walter Bright wrote:
>>
>> Please keep in mind that the following works today:
>>
>> void foo(int);
>>
>> template tuple(T...) { enum tuple = T; }
>>
>> void test() {
>> auto t = tuple!(1, 2, 3);
>> static foreach (i, e; t)
>> foo(e + i);
>> }
>>
>> and generates:
>>
>> void test() {
>> foo(1); foo(3); foo(5);
>> }
>
> not on 64bit.
> on 64bit it's cannot pass argument cast(ulong)__t_field_0 + 0LU of type ulong to parameter int.
> on 32 bit it genrates
> (int, int, int) t = tuple(1, 2, 3);
> foo(cast(ulong)__t_field_0 + 0LU);
> foo(cast(ulong)__t_field_1 + 1LU);
> foo(cast(ulong)__t_field_2 + 2LU);
Edit cast(ulong) -> cast(uint)
|
April 24, 2020 Re: I dun a DIP, possibly the best DIP ever | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter Bright | On Friday, 24 April 2020 at 08:15:53 UTC, Walter Bright wrote:
> On 4/24/2020 12:39 AM, Manu wrote:
>> It doesn't make much sense to think in terms of primitive types. As I just said (but you truncated), operator overloads are almost certainly part of this equation.
>
> Operator overloads don't change it.
Sure they do - just think of expression templates.
--
Simen
|
April 24, 2020 Re: I dun a DIP, possibly the best DIP ever | ||||
---|---|---|---|---|
| ||||
Posted in reply to Manu | On Wednesday, 22 April 2020 at 12:04:30 UTC, Manu wrote:
> We have a compile time problem, and this is basically the cure. Intuitively, people imagine CTFE is expensive (and it kinda is), but really, the reason our compile times are bad is template instantiation.
>
> This DIP single-handedly fixes compile-time issues in programs I've written by reducing template instantiations by near-100%, in particular, the expensive ones; recursive instantiations, usually implementing some form of static map.
>
> https://github.com/dlang/DIPs/pull/188
>
> This is an RFC on a draft, but I'd like to submit it with a reference implementation soon.
>
> Stefan Koch has helped me with a reference implementation, which has so far
> gone surprisingly smoothly, and has shown 50x improvement in compile times
> in some artificial tests.
> I expect much greater improvements in situations where recursive template
> expansion reaches a practical threshold due to quadratic resource
> consumption used by recursive expansions (junk template instantiations, and
> explosive symbol name lengths).
> This should also drastically reduce compiler memory consumption in
> meta-programming heavy applications.
>
> In addition to that, it's simple, terse, and reduces program logic indirection via 'utility' template definitions, which I find improves readability substantially.
>
> We should have done this a long time ago.
>
> - Manu
There is more corner cases to consider: AliasSeq-like members and tuple slicing.
What happens when a struct (or whatever) has a AliasSeq-like member, most prominently std.typecons.Tuple.expand.
Tuple!(short, int, long) t = tuple(short(1), int(2), long(3));
writeln(AliasSeq!("x", t.expand)); // => x 1 2 3
writeln(AliasSeq!("x", t.expand...)); // => x 1 2 3 (unambiguously, although semantics already unclear)
writeln(AliasSeq!("x", t.expand)...); // => ? does the ...-operator inspect the member to know it is a AliasSeq-like?
writeln(AliasSeq!("x", ((() => t)()).expand)...); // ?
writeln(AliasSeq!("x", ({
static if(t.expand == 1) {
return t
} else {
return tuple(4, 5, 6);
}
}()).expand)...); // it can't possibly expand here because the inner tuple already depends on the ..-expansion of t.expand == 1
The only sane way seems to be to treat .expand and any other member as opaque to-be-determined entity which is skipped by ...-expansion. Otherwise you get a inconsistency between cases 3 and 4/5. You have to explicitly opt-in by doing:
alias e = t.expand;
writeln(AliasSeq!("x", t.expand)); // => x 1 x 2 x 3
The other corner case is AliasSeq-slicing.
writeln(AliasSeq!("x", AliasSeq!(1, 2, 3))...); // no named sequence, no expansion => x 1 2 3
writeln(AliasSeq!("x", e)...); //named, therefore must be expansion => x 1 x 2 x 3
writeln(AliasSeq!("x", e[1..$])...); // ?
Is the last case of e[1..$] just an expression that happens to evaluate to an AliasSeq (like example here), or more like a named expression like example 2 here?
These cases also need to specified in the DIP.
|
April 24, 2020 Re: I dun a DIP, possibly the best DIP ever | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter Bright Attachments:
| On Fri, Apr 24, 2020 at 6:25 PM Walter Bright via Digitalmars-d < digitalmars-d@puremagic.com> wrote:
> On 4/23/2020 11:00 PM, Manu wrote:
> > I guess this is the key case you need to solve for:
> >
> > template T(Args...) {}
> > T!(Tup) -> T!(0, 1, 2)
> > T!(Tup)... -> T!0, T!1, T!2
> >
> > And the parallel expansion disambiguation is also critical:
> > T!(Tup, Tup2...)... -> T!(0, 3, 4, 5), T!(1, 3, 4, 5), T!(2, 3, 4, 5)
> >
> > If you can solve those, the rest will probably follow.
>
> Fair enough. Though there needs to be a rationale as to why those two
> particuler
> cases are needed and critical.
>
That is essentially the whole thing, simmered down to it's essence. Solve
those, we have everything.
If you can't solve those, we'll have a sea of non-uniformity, edge cases,
workarounds, and weird shit; classic D.
I haven't had experience with this feature yet; I don't know what's going
to emerge... but the last thing D needs is more weird edge cases.
But this is a clean, uniform, and very efficient design. I would be
disappointed to accept anything less.
I completely understand your motive, it's exactly where I started too. I couldn't find a way to resolve the ambiguity issues, and I'm not really interested in proposing a highly compromised design with workarounds to suit various usage contexts; that's just mental baggage you have to carry to solve application in all the different usage scenarios.
I'd like you to solve the cases I show above with your proposal. Find a way to do that, I'm super into your solution. If not, then I'm pretty invested in my DIP as specced.
Also, can you show all the cases where expansion can and can't occur under
your proposal?
These are cases where it must be inhibited:
1. calling function (because overloads/varargs, risk of hijacking)
2. template instantiations (because variadic arguments)
3. UFCS (because overloads + varargs)
4. array indexing (because overloaded opIndex for N-dimensional arrays)
We also lose the expansion for static fold.
Are there more applications that we lose?
It's possible that it must be inhibited ANYWHERE left of a `.` because to
the right might be a function, and/or because UFCS, we can not allow it due
to risk of hijacking :/
I don't think auto-expand is safely applicable to enough contexts. I really
think the explicit syntax is a better approach, and I'm interested in
seeing how to apply the expand syntax to arrays for uniformity.
|
April 24, 2020 Re: I dun a DIP, possibly the best DIP ever | ||||
---|---|---|---|---|
| ||||
Posted in reply to Manu | On Friday, 24 April 2020 at 09:52:48 UTC, Manu wrote:
> I don't think auto-expand is safely applicable to enough contexts. I really
> think the explicit syntax is a better approach, and I'm interested in
> seeing how to apply the expand syntax to arrays for uniformity.
I also think we should make it explicit. Makes it far better.
Otherwise I'll never trust any [] expression I'll see; always wondering: "does this expand?". It is likely the implementation is easier as well.
|
April 24, 2020 Re: I dun a DIP, possibly the best DIP ever | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter Bright | On 4/24/20 12:22 AM, Walter Bright wrote:
> On 4/22/2020 6:18 AM, Steven Schveighoffer wrote:
>> int double_int(int val) { return 2 * val; }
>>
>> T double_int(T val) { return val; }
>>
>> void double_ints(alias pred, T... args) {
>> pred(double_int(args)...);
>> }
>
> This can be done with the array syntax proposal using UFCS:
>
> void double_ints(alias pred, T... args) {
> args.pred();
> }
What's the array syntax proposal? The above seems not to invoke double_int at all.
-Steve
|
April 24, 2020 Re: I dun a DIP, possibly the best DIP ever | ||||
---|---|---|---|---|
| ||||
Posted in reply to Simen Kjærås | On 4/24/20 1:16 AM, Simen Kjærås wrote: > On Friday, 24 April 2020 at 00:16:52 UTC, Manu wrote: >> On a slightly unrelated note; there's a really interesting idea up-thread >> about making nested `...` not be expanded by the outer `...` >> For instance: >> >> template T(A, B...) { ... } >> T!(Tup1, Tup2...)... > > I'm wondering if perhaps that's the wrong way around - AliasSeq already does that: > > T!(Tup1, AliasSeq!Tup2)... // Should only expand Tup1 No, this means: T!(Tup1[0], AliasSeq!(Tup2[0])), T!(Tup1[1], AliasSeq!(Tup2[1]), ... > It might be that the more useful behavior is for ...-tuples to be expandable: > > T!(AliasSeq!(int, char)..., AliasSeq!Tup2)... // Should expand to T!(int, Tup2), T!(char, Tup2) > I think the suggestion quoted by Manu is more useful, we should ignore any inner ... when expanding an outer one. This gives maximum flexibility. If you want something that's not considered a tuple to be considered a tuple, you have the option of declaring it into a tuple before the expression. If you want to go the other way (prevent expansion of a tuple with ...), without this idea there's no recourse (well, you could do a mixin, but that would suck). -Steve |
April 24, 2020 Re: I dun a DIP, possibly the best DIP ever | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On 4/24/20 8:08 AM, Steven Schveighoffer wrote:
> On 4/24/20 12:22 AM, Walter Bright wrote:
>> On 4/22/2020 6:18 AM, Steven Schveighoffer wrote:
>>> int double_int(int val) { return 2 * val; }
>>>
>>> T double_int(T val) { return val; }
>>>
>>> void double_ints(alias pred, T... args) {
>>> pred(double_int(args)...);
>>> }
>>
>> This can be done with the array syntax proposal using UFCS:
>>
>> void double_ints(alias pred, T... args) {
>> args.pred();
>> }
>
> What's the array syntax proposal? The above seems not to invoke double_int at all.
OK, I now see there's a huge giant thread about this later ;)
so I think you mean this:
double_int(args).pred;
Which.... I don't know if this is what we want to support as a tuple expansion automatically (vs. compiler error)
-Steve
|
April 24, 2020 Re: I dun a DIP, possibly the best DIP ever | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter Bright | On 4/24/20 12:15 AM, Walter Bright wrote: > On 4/22/2020 5:04 AM, Manu wrote: >> [...] > > Ok, I've had a chance to think about it. It's a scathingly brilliant idea! > > But (there's always a but!) something stuck out at me. Consider arrays: > > > void test() > { > auto a = [1, 2, 3]; > int[3] b = a[]*a[]; // b[0] = a[0]*a[0]; b[1] = a[1]*a[1]; b[2] = a[2]*a[2]; > int[3] c = a[]*2; // c[0] = a[0]*2; c[1] = a[1]*2; c[2] = a[2]*2; > } > > These look familiar! D tuples already use array syntax - they can be indexed and sliced. Instead of the ... syntax, just use array syntax! Hm... but how do you know where to expand the expression and where to stop? With Manu's proposal: foo(bar(T)...) -> foo(bar(T[0]), bar(T[1])) foo(bar(T))... -> foo(bar(T[0])), foo(bar(T[1])) With array proposal: foo(bar(T)) -> which one? > > The examples from the DIP: > > ===================================== > --- DIP > (Tup*10)... --> ( Tup[0]*10, Tup[1]*10, ... , Tup[$-1]*10 ) > > --- Array syntax > Tup*10 > > ==================================== > --- DIP > alias Tup = AliasSeq!(1, 2, 3); > int[] myArr; > assert([ myArr[Tup + 1]... ] == [ myArr[Tup[0] + 1], myArr[Tup[1] + 1], myArr[Tup[2] + 1] ]); > > --- Array > alias Tup = AliasSeq!(1, 2, 3); > int[] myArr; > assert([ myArr[Tup + 1] ] == [ myArr[Tup[0] + 1], myArr[Tup[1] + 1], myArr[Tup[2] + 1] ]); Why is it not assert( [myArr[Tup + 1] ] == [ myArr[Tup[0] + 1]], myArr[Tup[1] + 1]] I think you absolutely need the tag to say which expressions are affected. An important thing to note is that we can't store expressions from statement to statement. This doesn't work (assuming array proposal): alias arrayParams = myArr[Tup + 1]; // error, can't read blah blah blah at compile time. auto newArr = [arrayParams]; // would be the equivalent of [ myArr[Tup + 1]... ] Another big problem is that tuples already bind to parameter lists, so this would be a breaking change or else be crippled for many uses. e.g.: foo(int x, int y = 5); alias seq = AliasSeq!(1, 2); foo(seq) -> foo(1, 5), foo(2, 5) or foo(1, 2)? -Steve |
Copyright © 1999-2021 by the D Language Foundation