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