Thread overview
Sixth Draft: Primary Type Syntax
5 days ago
Quirin Schroll
3 days ago
IchorDev
18 hours ago
Quirin Schroll
5 days ago

The permalink and current state

I hope that this draft finally is the last one.


The new section is Ambiguities Left to Maximum Munch. I added it because Walter strongly suggested me to spell out these details when I attended one of the Monthly Meetings.

3 days ago

On Saturday, 18 January 2025 at 05:13:18 UTC, Quirin Schroll wrote:

>

The new section is Ambiguities Left to Maximum Munch. I added it because Walter strongly suggested me to spell out these details when I attended one of the Monthly Meetings.

First off, I’m still desperate to have this feature! However, I have a potential syntax improvement for your consideration, with a bit of preamble:
What if we eventually deprecate the const(int)* syntax in a few editions, or at least prefer for people to use (const int)* for new code? Then the syntax would be more consistent across the whole language.
However, types like(const (shared int)*) present a problem since they’d be parsed as const(shared int)*, leaving people stuck with a confusing mixture of the two syntaxes; with one applied as a bandaid over the other.
So, the proposed syntax:
What if a type that is wrapped in parentheses (as in, with no TypeCtor prefix) would not parse the existing TypeCtor ( Type ) syntax inside it, instead only using ( Type )? For example:
const (shared int)* const applies to the pointer
(const (shared int)*) const doesn’t apply to the pointer
This would keep backwards compatibility but allow the primary type syntax to be more cohesive. Thoughts?

18 hours ago

On Monday, 20 January 2025 at 10:53:14 UTC, IchorDev wrote:

>

On Saturday, 18 January 2025 at 05:13:18 UTC, Quirin Schroll wrote:

>

The new section is Ambiguities Left to Maximum Munch. I added it because Walter strongly suggested me to spell out these details when I attended one of the Monthly Meetings.

First off, I’m still desperate to have this feature!

Thanks. My time around November and December was quite limited. On Christmas I tried to spend as much time with friends and family, so D takes a backseat. And I partially moved to a new project, so lots of overtime and less to spare for D, including this proposal.

>

However, I have a potential syntax improvement for your consideration, with a bit of preamble:
What if we eventually deprecate the const(int)* syntax in a few editions, or at least prefer for people to use (const int)* for new code? Then the syntax would be more consistent across the whole language.
However, types like(const (shared int)*) present a problem since they’d be parsed as const(shared int)*, leaving people stuck with a confusing mixture of the two syntaxes; with one applied as a bandaid over the other.
So, the proposed syntax:
What if a type that is wrapped in parentheses (as in, with no TypeCtor prefix) would not parse the existing TypeCtor ( Type ) syntax inside it, instead only using ( Type )? For example:
const (shared int)* const applies to the pointer
(const (shared int)*) const doesn’t apply to the pointer
This would keep backwards compatibility but allow the primary type syntax to be more cohesive. Thoughts?

I have thought about the issue of backwards compatibility a lot and removed part of the proposal that details out the possible idea to differentiate const(…) and const (…) and why it’s bad. Subtle differences like that trip new programmers (I don’t mean elite programmers new to D, but programming novices). Also, a syntax highlighter might then want to display const differently in const(…) and const (…) to aid in not tripping a programmer, but that’s nontrivial. Your idea falls into the same category that it makes the language harder to grasp in relation to qualifiers. Putting parentheses around something that’s already grouped shouldn’t make a difference. In C++, some people write return(result); and there are contexts in which that’s different from return result;, and that has led to hard-to-debug bugs. Your idea leads to things having a different type than current D programmers expect (it’s the whole point of the idea). D is more resilient when programmers get things wrong, thus the likelihood of such bugs is lower, but even in the happy case where it leads to a compile error, it could be frustrating.

I think I get where you’re coming from. The (…) types are new, so there isn’t any legacy code with them, and that means we can treat the (…) in any way we want. Technically, that’s true (I guess).

But even from the technical/formal side, it’s not fun. It would make parsing non-local. To put it in (simplified) formal grammar:

The core idea of my grammar proposal is this recurrence:

Type:
  Qualifiers? BasicType Suffixes?

BasicType:
  FundamentalType
  Qualifier? ( Type )

What you suggest:

Type:
  Qualifiers? BasicType Suffixes?

BasicType:
  FundamentalType
  Qualifier ( Type )
  ( TypeP )

TypeP:
  Qualifiers? BasicTypeP Suffixes?

BasicTypeP:
  FundamentalType
  ( TypeP )

The parse tree of (const (shared int)*) would look like this:

Type → Qualifiers? BasicType Suffixes?
 ├ Qualifiers? → ε
 ├ BasicType → ( TypeP )
 │  └ TypeP → Qualifiers? BasicTypeP Suffixes?
 │     ├ Qualifiers? → const
 │     ├ BasicTypeP → ( TypeP )
 │     │  └ TypeP → Qualifiers? BasicTypeP Suffixes?
 │     │     ├ Qualifiers? → shared
 │     │     ├ BasicTypeP → FundamentalType
 │     │     │  └ FundamentalType → int
 │     │     └ Suffixes? → ε
 │     └ Suffixes? → *
 └ Suffixes? → ε

I know I suggest a spec shortcut in the grammar in my DIP with requiring exactly one callable suffix after ref, but I do offer the full-glory grammar and if other people choose to do so, it can be on the dlang website (I care little); the actual grammar is a lot of noise, but it’s not a near-duplication of the type grammar with subtle differences. Even if the DMD code can use a boolean parameter for knowing if it’s P or not, some users would want the actual grammar on the spec because it’s subtle.

What I think really kills this idea, though, is existing D programmers. There already is code with the token sequence (const(int)*) namely parameter lists such as void function(const(int)*). So the eyes of an experienced D programmers never read const(int)* as anything but a pointer to a const int. Having to read that differently sometimes especially in niche contexts is asking a little too much, I fear.

One might think a new edition could get rid of the Maximum Munch exception, but in all honesty, I doubt it. That depends on what code makes it into the future. If by some date, const(int)* is some weird cringe only very old code of very outdated editions uses, it can work to deprecate it.

I even argue in the DIP why sticky qualifiers aren’t that bad, which – and that might surprise you – surprised me: “Obviously,” sin 2π = 0 and isn’t the same as sin(2)π = 2.856… in mathematics. It’s so obvious, in fact, most people don’t even realize how it works until it’s pointed out to them.

Anyway, thank you for the suggestion. I think unless you or someone else can come up with some good counter-points or other arguments, I’ll likely not integrate it.