Hi Benjamin, Thanks for thinking through this.
I'd push back on the declare approach. > I'd prefer to land with a version without declare's, but in this instance I > fear we need a temporary solution to get this off the ground until all the > pieces are in place. The "temporary until all the pieces are in place" framing doesn't quite hold. There's no committed path or timeline for reified generics, no implementation work currently in flight, and no clear answer to whether the runtime cost of reification can be brought to acceptable levels. A declare added on the assumption that it transitions to something else later would, realistically, remain in PHP for the indefinite future. PHP doesn't remove declare directives; `declare(ticks)` remains in the language decades after anyone uses it (at least as far as I'm aware). > declare(erased_types=generic_only); > > If this is not specified, then using generic syntax will throw an exception. Requiring `declare(erased_types=...)` to use the syntax raises the adoption barrier rather than lowering it. The point of native generics over docblocks is that the syntax should be easier and more discoverable, not require ceremony at the top of every file. A library exposing generic APIs would need the `declare` in every file. Consumers would copy-paste it as boilerplate, and new users would hit "why isn't my generic syntax working" errors and have to learn about a `declare` they didn't need to know about. `declare(strict_types=1)` has already shown how "opt-in via declare" plays out in practice. It shipped as a per-file choice but the ecosystem turned it into mandatory boilerplate. Linter rules, style guides, and codebase conventions all push toward "every file gets the declare." I have a linter rule that adds it to every PHP file in my projects, including config files that just `return [...];`, because remembering when it does and doesn't matter is mental overhead I'd rather not pay. Most serious PHP teams I know operate the same way. Adding `declare(erased_types=...)` would create the same dynamic within months, except now codebases have two declares to maintain consistency on, and any file using generic syntax errors without it. You'd ship a feature that's nominally opt-in but ecosystem-mandatory, which is the worst of both worlds. > And it could give the option in the future to add two modes: > > declare(erased_types=all); > declare(erased_types=none); The granularity is wrong for the future direction you're imagining. If reified generics ever ship, the right opt-in is per-class or per-function, not per-file. The strictness-vs-cost tradeoff is a code-level decision; a single class wanting reified semantics shouldn't require everything else in the file to share that choice. Hack made this exact decision: their `reify` keyword applies per-type-parameter (`function foo<reify T>(T $x)`), not per-file. The granularity matches the design space. `strict_types` is the PHP precedent worth comparing to, but in a different way than your proposal uses it. It's per-file because scalar coercion is a clear binary with a sensible default. Generics doesn't have a clear binary; it has a spectrum (fully erased -> bound erased -> reified -> fully dependent?), and the design space for "what gets enforced at runtime" is still being explored. Locking in a syntactic mechanism now for a feature whose semantics aren't settled would constrain future RFCs unnecessarily. The RFC as written doesn't preclude any of the directions you're imagining. If reified generics ever ships, it'll ship with an opt-in mechanism that fits the granularity of the actual feature, debated and decided at the time it's proposed. That's the right place to make that decision, not pre-loaded into this RFC. Cheers, Seifeddine.
