Re: [RFC] [Discussion] Bound-Erased Generic Types
| From: | Zebulan | Date: | Fri, 15 May 2026 15:55:37 +0000 |
| Subject: | Re: [RFC] [Discussion] Bound-Erased Generic Types | ||
| References: | 1 2 3 4 5 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-130893@lists.php.net to get a copy of this message | ||
On Friday, May 15th, 2026 at 7:13 AM, Nicolas Grekas <nicolas.grekas+php@gmail.com> wrote:
> I'd like to put a syntax proposal on the table that may address Rowan's concern, and
> that also addresses something I'm worried about independently: the migration path for existing
> libraries.
> How does a library that uses @template docblocks today migrate to native syntax without forcing
> a BC break on its consumers? Multiplied across the libraries out there, this is a major point of
> tension for the ecosystem we need to anticipate.
> Existing @template annotations were adoptable gradually *because* they're invisible to the
> engine.
> Native <T> syntax, as the RFC proposes it, doesn't have that property: a library
> cannot adopt it without bumping its minimum PHP version and pulling all its consumers with it.
> We've solved this exact problem once before for attributes. The #[...] syntax was
> deliberately designed so older PHP versions would parse it as a # line comment, which is what made
> the adoption across the ecosystem so smooth.
> The same trick is available for generics if we pick the right delimiter. Concretely, write
> #<...> everywhere <...> appears in the current RFC: declarations, inheritance clauses,
> type uses in signatures, call-site arguments. One syntax, used uniformly.
> Three benefits, beyond the migration story:
>
> 1. FC for free. A library can adopt native generics in source today and continue running on
> older PHP versions, because the engine just sees comments. No need to coordinate with the min-PHP
> bump.
> 2. The turbofish goes away. No need to disambiguate < from less-than comparison. With
> #<...>, the token is unique and unambiguous everywhere: at declaration, use, and call site. We
> get to drop a whole grammar mechanism rather than introduce one.
> 3. Rowan's concern is addressed typographically. Anything inside #<...> is the
> erased, SA-enforced layer; everything outside follows the engine's normal runtime-checked
> rules.
>
> For codebases that want to adopt native generics while still supporting earlier PHP versions,
> #<...> would need to sit at the end of a line so older parsers consume it as a # line comment.
> Code targeting only generics-aware PHP can write it inline. The line-break constraint is a
> transitional code-style cost, not a permanent property of the syntax, and it's bounded by
> however long libraries support pre-generics versions.
> WDYT? I expect Seifeddine has good reasons to prefer the current syntax. I'd like to put
> this on the table because the FC story it unlocks, combined with the turbofish simplification, might
> be worth the trade-off and might help gather a broader consensus.
> Cheers,
> Nicolas
>
How would generic types be expressed in parameters and return types?
```php
// Current RFC:
function DoStuff<T>(myParam: T, otherParam: int): T {
// ...
}
```
Existing PHP versions will have no clue what
T is. Only one of the uses of
T here is inside <...>. Wrapping the others in
#<...> would be syntactically incoherent (and rather ugly, I think). But the only
alternative that comes to mind is:
```php
function DoStuff#<T>(myParam#: T, otherParam: int)#: T {}
// which would have to look like this in projects continuing to support prior PHP versions:
function DoStuff#<T>
(
myParam#: T
, otherParam: int // bizarre comma placement
)#: T
{
// ...
}
```
Syntax parsing would, I suspect, be rather more complicated, unless you required the type to be
placed in parentheses, which would only make the syntax even less appealing:
```php
function DoStuff#<T>(myParam#:(T), otherParam: int)#:(T) {}
// and in projects continuing to support prior PHP versions:
function DoStuff#<T>
(
myParam#:(T)
, otherParam: int
)#:(T)
{
// ...
}
```
If not for those (major, in my view) syntactical issues, I might've been on board with the
idea. (Just to be clear, I don't have voting privileges.) Conceptually, it feels proper to use
"#" again for structured metadata that has *some* engine-enforced functionality, but which
is primarily targeted at (static analysis) developer tools. But once one realizes that generics will
be used in places outside of <...>, I think the viability of the syntax crumbles,
and I'd rather just have the original proposal.