D needs a type expression syntax

Quirin Schroll qs.il.paperinik at gmail.com
Sat May 6 15:55:22 UTC 2023


On Friday, 5 May 2023 at 02:30:32 UTC, Basile B. wrote:
> On Thursday, 4 May 2023 at 15:40:20 UTC, Quirin Schroll wrote:
>> **TL;DR:** If we make `TypeCtor` optional in the production 
>> rule `BasicType` → `TypeCtor`**`(`**`Type`**`)`**, the D type 
>> grammar can be improved and we have a way to solve the 
>> 14-year-old [issue 
>> 2753](https://issues.dlang.org/show_bug.cgi?id=2753).
>>
>> [...]
>
> Wouldn't making `ref` a `TypeCtor` solve the issue more simply?

Technically, the answer is yes, but practically and 
realistically, the answer isn’t just no, it is: absolutely not.

> While using parens is the natural way to disambiguate two 
> different constructs, it seems that the only case that causes a 
> problem is that we cannot express "return by ref" in function 
> types.

That was the starter. I looked at various ways you could express 
that. I’m convinced I once wrote a forum post asking for opinions 
on a syntax to propose, but now I cannot find it anymore (and 
that makes me doubt my memory). At some point, it randomly 
occurred to me that making D’s type syntax an expression syntax 
might naturally solve this – and working it out, it turns out it 
does.

> With `ref` as a `TypeCtor`:
> ```d
> // a function that returns a function ptr that returns an int 
> by ref
> ref(int) function(int) makesFPbyRef();
> // function ptr as a parameter type thar returns an int by ref
> void takesFP(ref(int) function() funcPtr)
> // function ptr as a ref parameter type thar returns an int by 
> ref
> void takesFP(ref ref(int) function() funcPtr)
> // normal variable type, can be a NOOP or a semantic error
> struct S { ref(int) member; }
> ```
>
> That being said, I understand that your proposal is not only 
> about the ref issue but to make the grammar for `Type` nicer.

It has `ref` as its main motivation.

First, there’s a difference between making `ref` a `TypeCtor` and 
making it a type constructor. The first is grammar/syntax-only 
and the other is a semantic language construct. I’ll assume you 
mean both; listing it as a `TypeCtor` without actually following 
up semantically would definitely be possible, but very confusing.

I’m proposing a grammar change that is mere addition. The 
deprecations I suggested are entirely optional and not required 
to make the syntax work. Also, I’m proposing a grammar change, no 
semantics change.

Looking at `struct S { ref(int) member; }`, it really seems you 
propose that `ref` be a full-fledged type constructor: Look no 
further than C++ – they have a reference type constructor and in 
C++, essential (core!) language and library constructs don’t work 
with references: Pointer to a reference and (as a logical 
consequence) an array of references are not well-formed as a 
type. A non-compositional type system is the last thing D needs.

I’ve thought long about this. **`ref(`***`Type`***`)`** as a 
syntax construct doesn’t get you far.
```d
void takesFP(ref ref(int) function() funcPtr)
```
This is akin to member functions written like this:
```d
const const(int) f() { … }
```
It’s not a nice read.

---

> Then, what is following will be a bit off-topic but, I'd like 
> to bring the fact that it might be desirable to keep parens for 
> builtin tuples, although it's been a while that the topic was 
> not discussed here. In particular a `TypeTuple` made of a 
> single `Type` would be hard to express properly.

This is no issue at all. With parentheses for tuple syntax, the 
1-tuple for *values* must be expressed as `(value,)` because 
`(value)` is a plain expression, so it would just be consistent 
to require the trailing comma with *types* as well: `(int)` is 
`int`, but `(int,)` is a 1-tuple with an `int` component.

For the record: I’ve objected to parentheses for tuples from the 
beginning. Parentheses are for grouping, not for constructing. My 
take on the tuples front was to look at static arrays as the 
special case of tuples that are homogeneous (the same type 
iterated), and generalize them for heterogeneous components: 
`int[2]` is a shorthand for `[int, int]`; and `[int, string]` is 
a heterogeneous tuple. You build such a tuple like you build an 
array: `[1, "abc"]`. Indexing with a run-time index is supported 
by Design by Introspection: A `[int, immutable int]` can give you 
`ref const(int)` access, a `[int, long]` can give you `long` 
access by value, but no `ref` access.


More information about the Digitalmars-d mailing list