Thoughts on possible tuple semantics

Dicebot public at dicebot.lv
Wed Aug 21 08:22:52 PDT 2013


Inspired by recent syntax thread I decided to write down by 
vision of how native language tuples should behave and how it may 
integrate into existing language state. There are lot of tricky 
corner cases and I'd like to see some more opinions on topic 
before trying DIP with full deprecation path design.

I didn't care about syntax or exact tuple features such as 
unpacking. Main questions for me were "what this thing actually 
is? and "how it can be expressed in existing language terms?"

----- Core -----

Define two distinct built-in "tuples":

     - Template argument sequence: may contain anything that is 
legal template
         argument.
     - Value tuple or simply "tuple": may contain any values, 
including run-time
     values. Value storage location is not defined.

Template argument sequence that contains only types is called 
"type sequence"
and is considered a type on its own. Type of value tuple is a 
type sequence.

Mixed template argument sequences are considered types too, but 
special one. Those
types can be aliased or passed to templates but can't be 
instantiated.

Each of these two entities has its own literal type. This is 
required to avoid
ambiguity between storing symbol as a type and taking its value 
on run-time. Tuple
always does the latter.

In further examples I will use imaginary syntax:
     ctseq(int, string, 42) : template argument sequence
     tuple("one", 2, three) : value tuple

>>>>> 

// connections to existing template syntax

void foo(T...)(T args)
{
     static assert(is(T == ctseq(int, string)));
     static assert(is(typeof(args) == T));
     assert(args == tuple(1, "2"));
     int a = 1;
     string b = "2";
     assert(args == tuple(a, b));
     static assert(typeof(tuple(a, b)) == ctseq(int, string));
}

foo(1, "2");

// type semantics of type sequence

ctseq(int, int) twoVars;
twoVars[0] = 42;
twovars[1] = 43;
assert(twoVars == tuple(42, 43));

ctseq(int, 42) twoVars; // compile-time error, can't instantiate 
mixed template argument sequence
assert(twoVars != ctseq(42, 42)); // NOT the same, breaking change

// compile-time vs run-time vs type semantics

auto a1 = tuple(42, 42); // ok
auto b1 = ctseq(42, 42); // error

enum a2_1 = tuple(42, 42); // ok
int a, b;
enum a2_2 = tuple(a, b); // error
enum b2 = ctseq(42, 42); // error, breaking change

alias a3 = tuple(42, 42); // error
alias b3 = ctseq(42, 42); // ok

<<<<<

----- (auto) expansion / packing / unpacking -----

As Andrei has stated clearly that he does not like auto-expansion 
and considers
it a major mistake, I was trying to imagine how that idea can be 
incorporated
into idiomatic D code.

>>>>> 

// existing syntax

// args can't be single entity and use normal parameter passing 
ABI at the same
// time.

void foo(T...)(T args) // following normal ABI implies unpacking
{
}

foo(1, 2, 3); // automatic packing

<<<<<

Breaking something like this does not seem reasonable. But I 
think salvation is
the "..." part. It may be explicitly defined to "implicit 
unpacking" and can be
used with palin tuple code like this:

>>>>> 

void boo(T)(T args)
{
     foo(args.expand);
}

boo(tuple(1, 2, 3));

<<<<<

One thing to consisder is .tupleof - should it result in actual 
tuple or maintain
current behavior? Former is probably more reasonable but it is 
even more break

struct S { int a, b, c; }

foo(S.init.tupleof.expand); // huh

.expand should be probably defined as a simple syntax rewrite:
     - tuple(a, b)" to "a, b" for literals
     - "tupleVar" to "tupleVar[0], tupleVar[1]" for variables

That also implies that packing / unpacking syntax, whatever it 
can be,
is completely unrelated to expansion - former can't be expressed 
as a simple
syntax rewrite, latter can't have special semantics tied to it 
without creating
even more meta-type to represent it.

----- ABI -----

Once built-in value tuples get recognized as a distinct entity, 
there is no
reason to now allow using them for return values or as 
un-expanded parameters.

All is needed is to define that tuple(a, b, c) has same ABI for 
return values
and parameters as a struct Tuple{ typeof(a) a; typeof(b) b; 
typeof(c) c; } - I have been
told that there are some issues with that approach but with no 
clear explanation.

Mangling question remains open.


----- std.typetuple.TypeTuple / std.typecons.Tuple -----

No need to keep them other than for backwards compatibility ;)


More information about the Digitalmars-d mailing list