Re: [RFC] [Discussion] Bound-Erased Generic Types

From: 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.

« previous php.internals (#130893) next »