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

From: Date: Mon, 11 May 2026 16:25:37 +0000
Subject: Re: [RFC] [Discussion] Bound-Erased Generic Types
References: 1 2  Groups: php.internals 
Request: Send a blank email to internals+get-130830@lists.php.net to get a copy of this message
Hi Rowan, I think the core premise is empirically testable, and the test has already run: PHP has had generics in docblocks for a decade, used by every major framework. If "someone publishes a library with broken type information and downstream users get surprised by runtime behavior" were a real failure mode, we would see it today, as Laravel, Doctrine, Symfony, PSL, and PHPUnit all run on @template-annotated code and are consumed by millions of downstream applications. The failure mode you describe is the one that would occur most under the current system, yet it doesn't. The reason it doesn't is that the people who care about generic type information are the same people who run static analysis. The intersection of "uses generics" and "doesn't run a static analyzer" is approximately empty in practice. Native syntax doesn't change that. It just gives current users a better way to express what they mean. Regarding the second point, the proposed implementation is enforced more at compile time than docblocks are. The engine validates: - Generic parameter declarations: arity cap, bound conformance, default-vs-bound, no top-level self-reference, no shadowing. - Variance soundness: Covariant parameters cannot appear in input positions, contravariant parameters cannot appear in output positions, at the declaration site. - Inheritance: arity at extends/implements/use, bound conformance with bound-on-bound for forwarded parameters, diamond detection, parametric LSP into properties/hooks/trait methods/inherited methods. - Turbofish at runtime: arity and bounds at every call site that supplies type arguments. Docblocks validate none of this; the engine accepts a docblock that says anything. Native syntax is a stricter contract than what the ecosystem has today. Finally, on the "broken code published to Packagist" concern: someone can already publish: ``` /** Map the given array using the provided callable. */ function map(array $array, callable $fn): array { return []; } ``` The signature claims to take an array and a callable, returns an array. The body returns an empty array regardless. Today, this passes every PHP type check, parses without error, runs in production, and is technically conformant. Nobody is calling for PHP to ship a [precious type system](https://www.youtube.com/watch?v=Ay-gnCqDw9o) with support for [lifetimes](https://doc.rust-lang.org/rust-by-example/scope/lifetime/explicit.html), and [const generic parameters](https://doc.rust-lang.org/reference/items/generics.html#r-items.generics.const.usage) to prevent it (e.g. `function map<'a, I, O, const SIZE: int>(vec<&'a I, SIZE> & ![] $array, fn(&'a I): &'a O $fn): vec<&'a O, SIZE> & ![]`). The same logic applies to generics: the contract a user writes is the contract a user is responsible for honoring, and PHP's role is to provide tools for expressing and checking that contract, not to make every conceivable mistake impossible. The options you listed both close off paths the PHP ecosystem has already shown it doesn't want. Cheers, Seifeddine.

« previous php.internals (#130830) next »