D needs a type expression syntax
Quirin Schroll
qs.il.paperinik at gmail.com
Fri May 12 17:13:51 UTC 2023
**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:
```d
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`](https://dlang.org/library/std/array/static_array.html) that makes literals become a static array type, we can have `asTuple` that makes literals become tuple types:
```d
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:
```d
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.
More information about the Digitalmars-d
mailing list