Jump to page: 1 2
Thread overview
April 09
Hello,

The DIP is to allow multiple auto ref return values.

After discussion with Ilya in this [1] thread, I'm looking forward to taking
a DIP as a GSoC project. This a summary of my discussion with Ilya.

So, right now, we can return a ref value:

ref int foo1(int[] a) {
        return a[0];
}

void main() {
        int[] a = [1,2,3];
        foo1(a) = 4;  // a[0] is now 4
}

and also an auto ref value, which is the usual: Automatically
infer if you should return an lvalue (aka ref) or a value.

To have multiple return values, the possible workarounds are:

-- a) Multiple out/ref parameters

So, let's say I want to return 1 int and 1 double (values, nothing related
to refs):
void foo2(ref int x, ref double y) {
        x = 1;
        y = 1.2;
}

void main() {
        int a;
        double b;
        foo2(a, b);  // a == 1, b == 1.2
}

-- b) Use std.type.Tuple

import std.typecons;
Tuple!(int, double) foo3() {
    return tuple(1, 1.2);
}

void main() {
        int a;
        double b;
        auto res = foo3();
        a = res[0];
        b = res[1];
}

Ilya:

> Both of them don't allow to return multiple values by reference: for
> example, references on array/container elements.

To return multiple ref values we could:

-- c. Add additoinal arguments as ref/out _pointer_ parameters

-- d. Use mir.functional.RefTuple.

Ilya:
> Both of them don't well fit to modern D, Mir, and future generic
> containers. For example, Mir's Series consist as pair of two arrays
> (keys and values), keys are sorted. It would be awesome to use D Range
> syntax (front, popFront, empty) to iterate Series by reference. Also,
> it would be very good for perfomance, for D GC-free reference-counted
> libraries.
>
> The possible (but may not be the best) syntax is:
>
> auto ref fun(int[] a)
> {
>     return (a[0], a[1] * 3.4); // returns ref int and double
> }
> 
> void handle(ref int a, double b)
> {
>     a = cast(int) (b * b);
> }
> 
> int twice(int a) { a * 2; }
> 
> void main()
> {
>     int[] ar = [1, 2];
>     handle(fun(ar));
>     auto (i : &$0, d, e : $0 + $1, f : $1.twice) = fun(ar);
> 
>     // i is a pointer to ar[0], type of int*
>     // d stores a values of a[1] * 3.4, type of double
>     // e stores value ar[0] + a[1] * 3.4, type of double
>     // f stores value ar[0] * 2, type of int
> }

So, then I proposed that we could internalize RefTuple as part of the syntax.
That is, when one writes:

> auto ref fun(int[] a)
>
> {
>
>     return (a[0], a[1] * 3.4); // returns ref int and double
>
> }

they should expect to get a RefTuple.

Then, Ilya pointed that:
> D has two kinds of struct tuples, expanded and not expanded.
> 
> 1. AliasSeq!(1, a, b,) - is expanded tuple.
> 2. tuple(1, a, b) - is not expanded tuple (just a struct)
> 3. tuple(1, a, b).expand is an expanded tuple where 'expand' is an alias sequence of structs members.
> 
> For not expanded return value the approach you have proposed is a solution.
> For already expanded return value the approach isn't required.

The problem that he pointed is that:

> The RefTuple/Tuple is a structure. So, a compiler should generate opAssign, and may also generate copy constructor and destructor.
> So returning expanded tuple should be implemented in compiler using a way that does not require to create a return type at all.
> I think expanded tuple is more preferable, more elegant, and does not require to incorporate a (Ref)Tuple struct implementation into the language.

---- FINALLY ----

Here are the two variants for a solution from my point of view:
1) Incorporate the expanded tuple into the language, so that no type is created.
2) Incorporate RefTuple into the language, so that when one returns multiple
ref values, he gets a RefTuple.

Waiting for opinions.



[1] https://forum.dlang.org/thread/pvhacqomqgnhmqienfpi@forum.dlang.org?page=3
April 09
On Tuesday, 9 April 2019 at 14:37:17 UTC, Stefanos Baziotis wrote:
> Hello,
>
> The DIP is to allow multiple auto ref return values.
>
> [...]

Just to make sure, you now there is also another dip touching tuples:

https://github.com/tgehr/DIPs/blob/tuple-syntax/DIPs/DIP1xxx-tg.md

Kind regards
Andre
April 09
On Tuesday, 9 April 2019 at 15:05:49 UTC, Andre Pany wrote:
> Just to make sure, you now there is also another dip touching tuples:
>
> https://github.com/tgehr/DIPs/blob/tuple-syntax/DIPs/DIP1xxx-tg.md

No, I didn't, thanks a lot Andre. From what I can understand, having tuples
built into the language does not solve the problem of returning multiple
auto ref values (or just multiple ref values), correct?

- Stefanos
April 09
On Tuesday, 9 April 2019 at 15:12:10 UTC, Stefanos Baziotis wrote:
> On Tuesday, 9 April 2019 at 15:05:49 UTC, Andre Pany wrote:
>> Just to make sure, you now there is also another dip touching tuples:
>>
>> https://github.com/tgehr/DIPs/blob/tuple-syntax/DIPs/DIP1xxx-tg.md
>
> No, I didn't, thanks a lot Andre. From what I can understand, having tuples
> built into the language does not solve the problem of returning multiple
> auto ref values (or just multiple ref values), correct?
>
> - Stefanos

It doesn't solve the issue but maybe there is some info which you can reuse for your dip or maybe you need some part from the other dip.

Kind regards
Andre
April 09
On Tuesday, 9 April 2019 at 15:20:58 UTC, Andre Pany wrote:
>
> It doesn't solve the issue but maybe there is some info which you can reuse for your dip or maybe you need some part from the other dip.

Definitely. I have read it about 4 times by now, and surely I don't in any way pretend to know how to write DIPs. :P

Best,
Stefanos
April 09
On 2019-04-09 17:12, Stefanos Baziotis wrote:

> No, I didn't, thanks a lot Andre. From what I can understand, having tuples
> built into the language does not solve the problem of returning multiple
> auto ref values (or just multiple ref values), correct?

Technically returning a tuple would be returning a single value. But the value is a collection of values, so it contains multiple values. With some syntax sugar to auto expand the tuple it's very similar, if not, exactly the same as returning multiple values.

-- 
/Jacob Carlborg
April 09
On Tuesday, 9 April 2019 at 18:15:30 UTC, Jacob Carlborg wrote:
> Technically returning a tuple would be returning a single value. But the value is a collection of values, so it contains multiple values. With some syntax sugar to auto expand the tuple it's very similar, if not, exactly the same as returning multiple values.

I might be wrong but:
The problem is not returning multiple values. The problem is returning multiple
ref values. Even if the tuple is built into the language, a tuple can't have ref
values because ref is not a type, a problem for which RefTuple was created.
RefTuple has difficult usage in an of itself for the users. As you can see, my
original proposal was to incorporate RefTuple to the language but RefTuple
is a struct and that comes with an overhead. So, the other idea is to do something
similar to what you say, i.e. auto-expand the tuple at compile-time but with the
support of ref values.
April 09
On 09.04.19 16:37, Stefanos Baziotis wrote:
> 
> 
> Here are the two variants for a solution from my point of view:
> 1) Incorporate the expanded tuple into the language, so that no type is created.

Expanded tuples already exist, but you can't return them from functions:

---
auto ref seq(T...)(return auto ref T values){
    return values;
}
---
Error: functions cannot return a tuple
---

I think the reason for this restriction is that

1) Walter originally wanted multiple return values to follow calling conventions similar to passing multiple parameters.
2) It would need special name mangling.

The DIP could try to lift this restriction while also allowing multiple `ref` return values.

> 2) Incorporate RefTuple into the language, so that when one returns multiple ref values, he gets a RefTuple.
> 
> Waiting for opinions.

I would presume that RefTuple does not work properly in @safe code? Your DIP probably would have to support `@safe` and `return` annotations. One question here will again be name mangling. (Which the tuple DIP itself sidesteps, but it seems a bit more complicated here)


In any case, this kind of thing should be an extension of the work-in-progress tuple DIP:
https://github.com/tgehr/DIPs/blob/tuple-syntax/DIPs/DIP1xxx-tg.md


Basically, on top of the tuple DIP, just handle this kind of thing:

---
void foo((ref int a, int b), int c){
    a=b+c;
}
(ref int, int) bar(return ref int x,int y){
    return (x,y);
}
void main(){
    auto x=(1,2), y=3;
    foo(x,y);
    assert(x==(5,2));
    x[1]+=1;
    foo(bar(x),y);
    assert(x==(6,3));
}
---

---
auto ref foo(return ref int x){
    return (x,1); // auto ref applies to individual fields if return value is a tuple literal
}

void main(){
    int x=2;
    foo(x)[0]+=1;
    assert(x==3);
    assert(foo(x)[1]==1);
}
---

---
import std.range, std.algorithm; // (need to be changed)
void main(){
    auto a=[1,2,3,4,5];
    foreach((i,ref j);enumerate(a)){
        a[i]=i;
    }
    assert(a==[0,1,2,3,4]);
}
---

Are you interested in DIP writing only or also compiler implementation?
My partial progress on a tuple DIP implementation is here:
https://github.com/dlang/dmd/compare/master...tgehr:tuple-syntax

It supports parts of the first three proposals from the DIP: unpacking variable declarations, calling functions with multiple parameters with a single tuple argument, and tuple literals.

April 09
On 09.04.19 23:05, Timon Gehr wrote:
> ---
> import std.range, std.algorithm; // (need to be changed)
> void main(){
>      auto a=[1,2,3,4,5];
>      foreach((i,ref j);enumerate(a)){
>          a[i]=i;
>      }
>      assert(a==[0,1,2,3,4]);
> }
> ---

Obviously, this should have been:

---
import std.range, std.algorithm; // (need to be changed)
void main(){
     auto a=[1,2,3,4,5];
     foreach((i,ref j);enumerate(a)){
         j=i;
     }
     assert(a==[0,1,2,3,4]);
}
---
April 09
On 09.04.19 23:07, Timon Gehr wrote:
> ...
> 
> Obviously, this should have been:
> 
> ---
> import std.range, std.algorithm; // (need to be changed)
> void main(){
>       auto a=[1,2,3,4,5];
>       foreach((i,ref j);enumerate(a)){
>           j=i;
>       }
>       assert(a==[0,1,2,3,4]);
> }
> ---

And if you want it to compile on 64 bits:

---
import std.range, std.algorithm; // (need to be changed)
void main(){
      size_t[] a=[1,2,3,4,5];
      foreach((i,ref j);enumerate(a)){
          j=i;
      }
      assert(a==[0,1,2,3,4]);
}
---

(I have sent this message before, but it does not seem to have made it to the server. I'm sorry if this is a double-post.)
« First   ‹ Prev
1 2