This is 100% off-topic now. This should be in its separate thread.
On Thursday, 11 May 2023 at 11:38:08 UTC, Timon Gehr wrote:
> On 08.05.23 13:21, Petar Kirov [ZombineDev] wrote:
> On Sunday, 7 May 2023 at 07:35:22 UTC, Timon Gehr wrote:
> [..]
Having T[2]
be a tuple type [T,T]
is one of those things that initially sound good enough on paper until you actually examine the existing array semantics that D already has and try to square them with tuple semantics. It does not work at all. D would need to break arrays. It's a pipe dream.
Can you explain in more detail which parts of the language make the unification impossible currently? Or to phrase the question differently, in a hypothetical D3 language what (breaking) changes we would need to do relative to D2 to make this possible?
I am not sure it is desirable if arrays are not all value types.
- If sliced,
T[n]
results in a T[]
. Tuple slices should not decay into by-reference array slices randomly based on whether element types match. Also affects array assignment syntax and array operations.
There’s two kinds of slices: xs[]
and xs[i .. j]
.
The first one should always return a slice type, i.e. T[]
for some T
.
Of course, you can’t slice a heterogeneous tuple like. You can’t slice it with run-time indices either. With indices known at compile-time, the result can be a tuple, but it won’t be, unless specifically asked to. It generally would become a slice, like for arrays:
int[5] xs;
auto ys = xs[2..4]; // typeof(ys) == int[]
int[2] zs = xs[2..4]; // You get int[2] if you ask for it.
If you change the type of xs
to Tuple!(long, int, int, int, int)
all of that should still work, but if you also replace e.g. 2
with a run-time variable, it would not work anymore.
>
- Type of array literals is
T[]
(uniform element type, dynamic length, reference type). It makes very little sense to have this and also have [int, string]
be a tuple type.
The actual type of a “braced literal” (let’s call it that) is neither T[]
nor T[n]
. It’s something internal to the compiler, something the D grammar has no syntax for.
It obviously isn’t T[n]
because if you ask a T[n]
its type, it answers T[n]
.
It is not a T[]
wither because – even though if you ask its type it answers T[]
– it can do things a T[]
generally can’t do: It can be converted to a T[n]
if requested to, it even infers n
; a general T[]
can’t do that.
“The type of braced literals” is a type of its own.
Why couldn’t a “braced literal” be extended so that it converts to a tuple if asked to? The only new thing would be that there would be literals that you can’t initialize an auto
variable with, but you can initialize a variable with it if its type is a tuple. That is because auto
never infers static array types and likewise never infers tuple types, thus auto
requires them to become a slice and they would need a common type for that. If there is none it’s a compile-error.
The same way we have staticArray
that makes literals become a static array type, we can have asTuple
that makes literals become tuple types:
auto xs = [1, 2.0]; // typeof(xs) == double[]
auto ys = [1, 2.0].staticArray; // typeof(ys) == double[2]
auto zs = [1, 2.0].asTuple; // typeof(zs) == [int, double]
auto bad = [new Object, 1]; // no common type for T[] of Object and int.
[Object, int] good1 = [new Object, 1]; // no type inference → no common type needed
auto good2 = [new Object, 1].asTuple; // typeof(good2) == typeof(good1)
Its implementation would be really simple:
auto asTuple(Ts...)([Ts...] tuple) => tuple;
// staticArray for comparison:
auto staticArray(T, size_t n)(T[n] array) => array;
>
- Tuple indices (presumably should) need to be statically known, array indices do not. Having
[T, S] t; e = t[i]
evaluate i
at compile time iff !is(T == S)
is potentially surprising behavior.
It sounds weird, but there is a kind of spectrum between truly heterogeneous tuples and arrays. There’s tuples with varying degrees of homogeneity.
“Tuple indices need to be statically known” Yes, if the tuple is something like Object
and int
because they are totally unrelated. (It could be a sum type, though.) A tuple of int
and long
is kind of in-between and a tuple of int
and immutable int
is as close to an array as it could be without actually being one.
I’d phrase it the other way around: Every tuple supports indexing if the index is known at compile-time, and – for the record – that indexing is always by reference. A tuple would conditionally support run-time indexing depending on how similar its types are. Run-time indexing can be by reference or by value, again depending on how similar its types are. A static array viewed as a tuple of repeated type just so happens to satisfy the conditions such that run-time indexing by reference is always possible. Some non-array tuples allow for run-time indexing by reference as well. It’s simple Design by Introspection:
- If the types have a common reference type (e.g.
int
and immutable(int)
can be referenced as const(int)
), run-time indexing returns const(int)
by reference.
- If the types have a common type (e.g.
int
and long
), run-time indexing returns long
by value.
The neat things is that if you expected run-time indexing behavior, but the index is available at compile-time, you might get a different type, but a type that’s better than the one you asked for.
If you expected indexing by value and it happens to be indexing by reference, the value is probably still copied; the exception is auto ref
.
On the off-chance that you really needed a very specific behavior, you can ensure to get it. The simple syntax will give you the best it can given the circumstances, i.e. the types of the tuple and the compile-time-ness of the index.
>
T[n]
has properties like .ptr
, dup
, idup
. It seems a bit weird to provide this for tuples and it's not clear what they should do. I guess enabling them conditionally is an option, but that seems wacky.
You generally don’t provide them for tuples. They would have those properties, if the types allow it. It’s just Design by Introspection. I’d not call that wacky.
>
void[n]
and void[]
. How do they fit into the tuple story?
My suggestion would be: T[n]
is a shorthand for [T, T, ...]
repeated n
times, unless T
is void
or n
is 0
. In those cases, T[n]
is its own type. The first one is because a void[n]
doesn’t store n
values of type void
, it’s untyped memory of n
bytes; you can slice it, but not index it. The second one is because T[0]
has an associated type. I can ask a Tuple!(int, int)
“Are the types of your elements the same type and if so, what is that type?” and get the answer: Yes, int
. If I ask Tuple!(int, long)
, the answer is no. If I ask Tuple!()
, the answer is yes, but there is no type!
We’d therefore have T[0]
separate from the type of the empty tuple, at least if T
is not void
. I guess we can make void[0]
the empty tuple type.
void[]
is unrelated, it’s a slice, not an array or tuple.