DIP for multiple auto ref values

Stefanos Baziotis sdi1600105 at di.uoa.gr
Tue Apr 9 14:37:17 UTC 2019


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


More information about the Digitalmars-d mailing list