July 03, 2020
On Thursday, 2 July 2020 at 20:26:15 UTC, Steven Schveighoffer wrote:
> On 7/2/20 3:28 PM, Nick Treleaven wrote:
>> On Thursday, 2 July 2020 at 15:47:39 UTC, Steven Schveighoffer wrote:
>>> a ~ b ~ c ~ d
>>>
>>> will translate to
>>>
>>> a.opConcatAll(b, c, d)
>> 
>> Sounds good, although I think multiple other operations beside concatenation could benefit from being intercepted:
>> 
>> opNary(string op, T...)(T args)
>> 
>> E.g. perhaps BigInt can avoid one or more reallocations for >2 args. The compiler would try opNary first in that case, and fallback to successive calls to opBinary as now.
>> 
>> In fact, maybe some types could benefit from intercepting different operations at once:
>> 
>> opNary(string[] ops, T...)(T args)
>
> That would be awesome, and cover my case!
>
> -Steve

We kind of have this already, but a bit closer to what jmh was describing earlier.
Though it's just an implementation detail inside druntime for implementing array ops:

https://github.com/dlang/druntime/blob/v2.092.1/src/core/internal/array/operations.d#L15-L36

I think that it would be pretty cool if this technique could be applied to user-defined types (e.g. allow library authors to provide their own `arrayOp` implementation of their types).
July 03, 2020
On 02.07.20 21:28, Nick Treleaven wrote:
> On Thursday, 2 July 2020 at 15:47:39 UTC, Steven Schveighoffer wrote:
>> a ~ b ~ c ~ d
>>
>> will translate to
>>
>> a.opConcatAll(b, c, d)
> 
> Sounds good, although I think multiple other operations beside concatenation could benefit from being intercepted:
> 
> opNary(string op, T...)(T args)
> 
> E.g. perhaps BigInt can avoid one or more reallocations for >2 args. The compiler would try opNary first in that case, and fallback to successive calls to opBinary as now.
> 
> In fact, maybe some types could benefit from intercepting different operations at once:
> 
> opNary(string[] ops, T...)(T args)

Maybe it could pass a single fully parenthesized expression instead of an array of operators.

But given D's philosophy of crippling operator overloading in the name of prevention of "abuse", something like this will likely never be added.
July 03, 2020
On Thursday, 2 July 2020 at 19:28:15 UTC, Nick Treleaven wrote:
> On Thursday, 2 July 2020 at 15:47:39 UTC, Steven Schveighoffer wrote:
>> a ~ b ~ c ~ d
>>
>> will translate to
>>
>> a.opConcatAll(b, c, d)
>
> Sounds good, although I think multiple other operations beside concatenation could benefit from being intercepted:
>
> opNary(string op, T...)(T args)
>
> E.g. perhaps BigInt can avoid one or more reallocations for >2 args. The compiler would try opNary first in that case, and fallback to successive calls to opBinary as now.
>
> In fact, maybe some types could benefit from intercepting different operations at once:
>
> opNary(string[] ops, T...)(T args)

Wow, that looked cool the first 3 secons but now I wander if  you really want to let the user break the rules of precedence ?
July 03, 2020
On 7/3/20 3:17 AM, Petar Kirov [ZombineDev] wrote:
> On Thursday, 2 July 2020 at 20:26:15 UTC, Steven Schveighoffer wrote:
>> On 7/2/20 3:28 PM, Nick Treleaven wrote:
>>> On Thursday, 2 July 2020 at 15:47:39 UTC, Steven Schveighoffer wrote:
>>>> a ~ b ~ c ~ d
>>>>
>>>> will translate to
>>>>
>>>> a.opConcatAll(b, c, d)
>>>
>>> Sounds good, although I think multiple other operations beside concatenation could benefit from being intercepted:
>>>
>>> opNary(string op, T...)(T args)
>>>
>>> E.g. perhaps BigInt can avoid one or more reallocations for >2 args. The compiler would try opNary first in that case, and fallback to successive calls to opBinary as now.
>>>
>>> In fact, maybe some types could benefit from intercepting different operations at once:
>>>
>>> opNary(string[] ops, T...)(T args)
>>
>> That would be awesome, and cover my case!
>>
>> -Steve
> 
> We kind of have this already, but a bit closer to what jmh was describing earlier.
> Though it's just an implementation detail inside druntime for implementing array ops:
> 
> https://github.com/dlang/druntime/blob/v2.092.1/src/core/internal/array/operations.d#L15-L36 
> 
> 
> I think that it would be pretty cool if this technique could be applied to user-defined types (e.g. allow library authors to provide their own `arrayOp` implementation of their types).

How are parens handled?
July 03, 2020
On Friday, 3 July 2020 at 15:39:08 UTC, Andrei Alexandrescu wrote:
> On 7/3/20 3:17 AM, Petar Kirov [ZombineDev] wrote:
>> On Thursday, 2 July 2020 at 20:26:15 UTC, Steven Schveighoffer wrote:
>>> On 7/2/20 3:28 PM, Nick Treleaven wrote:
>>>> [...]
>>>
>>> That would be awesome, and cover my case!
>>>
>>> -Steve
>> 
>> We kind of have this already, but a bit closer to what jmh was describing earlier.
>> Though it's just an implementation detail inside druntime for implementing array ops:
>> 
>> https://github.com/dlang/druntime/blob/v2.092.1/src/core/internal/array/operations.d#L15-L36
>> 
>> 
>> I think that it would be pretty cool if this technique could be applied to user-defined types (e.g. allow library authors to provide their own `arrayOp` implementation of their types).
>
> How are parens handled?

By multiple consecutive lowerings in order of parens?

(a op b) op c => t.op(a, b).op(c)

July 03, 2020
On 7/3/20 11:39 AM, Andrei Alexandrescu wrote:
> On 7/3/20 3:17 AM, Petar Kirov [ZombineDev] wrote:
>> On Thursday, 2 July 2020 at 20:26:15 UTC, Steven Schveighoffer wrote:
>>> On 7/2/20 3:28 PM, Nick Treleaven wrote:
>>>> On Thursday, 2 July 2020 at 15:47:39 UTC, Steven Schveighoffer wrote:
>>>>> a ~ b ~ c ~ d
>>>>>
>>>>> will translate to
>>>>>
>>>>> a.opConcatAll(b, c, d)
>>>>
>>>> Sounds good, although I think multiple other operations beside concatenation could benefit from being intercepted:
>>>>
>>>> opNary(string op, T...)(T args)
>>>>
>>>> E.g. perhaps BigInt can avoid one or more reallocations for >2 args. The compiler would try opNary first in that case, and fallback to successive calls to opBinary as now.
>>>>
>>>> In fact, maybe some types could benefit from intercepting different operations at once:
>>>>
>>>> opNary(string[] ops, T...)(T args)
>>>
>>> That would be awesome, and cover my case!
>>
>> We kind of have this already, but a bit closer to what jmh was describing earlier.
>> Though it's just an implementation detail inside druntime for implementing array ops:
>>
>> https://github.com/dlang/druntime/blob/v2.092.1/src/core/internal/array/operations.d#L15-L36 
>>
>>
>> I think that it would be pretty cool if this technique could be applied to user-defined types (e.g. allow library authors to provide their own `arrayOp` implementation of their types).
> 
> How are parens handled?

There are possibilities. The array vector operations seem to be passed in RPN, negating the need for parentheses handling (maybe the compiler translates the expression for you?)

Another possibility is just to stop at parentheses boundaries. e.g.:

(a + b + c) * d => a.opNary!(["+", "+"])(b, c).opBinary!"*"(d);

Which isn't as powerful, but still helpful in many cases.

-Steve
July 03, 2020
On 7/3/20 11:25 AM, user1234 wrote:
> On Thursday, 2 July 2020 at 19:28:15 UTC, Nick Treleaven wrote:
>> On Thursday, 2 July 2020 at 15:47:39 UTC, Steven Schveighoffer wrote:
>>> a ~ b ~ c ~ d
>>>
>>> will translate to
>>>
>>> a.opConcatAll(b, c, d)
>>
>> Sounds good, although I think multiple other operations beside concatenation could benefit from being intercepted:
>>
>> opNary(string op, T...)(T args)
>>
>> E.g. perhaps BigInt can avoid one or more reallocations for >2 args. The compiler would try opNary first in that case, and fallback to successive calls to opBinary as now.
>>
>> In fact, maybe some types could benefit from intercepting different operations at once:
>>
>> opNary(string[] ops, T...)(T args)
> 
> Wow, that looked cool the first 3 secons but now I wander if  you really want to let the user break the rules of precedence ?

Meh, the user can do anything he wants already. One can get around precedence by deferring calls.

It is true, though, that I didn't think of precedence when I first posted -- I just wanted to avoid some allocations.

-Steve
July 03, 2020
On Friday, 3 July 2020 at 17:13:32 UTC, Steven Schveighoffer wrote:
> [snip]
>
> Meh, the user can do anything he wants already. One can get around precedence by deferring calls.
>
> It is true, though, that I didn't think of precedence when I first posted -- I just wanted to avoid some allocations.
>
> -Steve

I see no reason why Aliak's suggestion above with respect to parentheses cannot be also used to ensure precedence is followed. For instance, if you have a + b * c, then it gets re-written to a + (b * c), which gets re-written to something like a.op1(b.op2(c)).
July 03, 2020
On 7/3/20 1:54 PM, jmh530 wrote:
> On Friday, 3 July 2020 at 17:13:32 UTC, Steven Schveighoffer wrote:
>> [snip]
>>
>> Meh, the user can do anything he wants already. One can get around precedence by deferring calls.
>>
>> It is true, though, that I didn't think of precedence when I first posted -- I just wanted to avoid some allocations.
>>
> 
> I see no reason why Aliak's suggestion above with respect to parentheses cannot be also used to ensure precedence is followed. For instance, if you have a + b * c, then it gets re-written to a + (b * c), which gets re-written to something like a.op1(b.op2(c)).

That's what's already done today.

The idea here is to pass all the operations and parameters at once. There are actually advantages in some cases to have the entire expression, as some things could be optimized by reordering operations (in a valid way).

The further question of this subthread is -- can we loosen some of the precedence rules so this provides more capabilities, or would it be crippled by enforcing precedence rules?

-Steve
July 03, 2020
On Thursday, 2 July 2020 at 19:28:15 UTC, Nick Treleaven wrote:
> In fact, maybe some types could benefit from intercepting different operations at once:
>
> opNary(string[] ops, T...)(T args)

What I'll say is off topic but what I wanted once is

  opDispatch(string[] members, T...)(T args)

To solve a whole identifier chain ine one shot.