Hi Nicolas,

I'd like to push back on the inheritance-BC framing. The claim that bounded
types have no sensible default or wildcard is not quite accurate; defaults
can be any type expression that satisfies the bound, including the bound
itself.

For example, consider this docblock:

```
/**
 * @template Id of string|object
 * @template T of UserInterface
 */
interface UserProvider {
  /**
   * @param Id $id
   * @return T|null
   */
 public function load(string|object $id): null|UserInterface;
}
```

Under this RFC, the migration would be:

```
interface UserProvider<
  Id : string|object = string|object,
  T : UserInterface = UserInterface,
> {
  public function load(Id $id): T|null;
}
```

By using the bounds themselves as defaults, every existing implementer
continues to work unchanged:

```
class DummyProvider implements UserProvider {
  public function load(string|object $x): ?UserInterface {
    return null;
  }
}
```

This is the general pattern: for any bounded type parameter, the widest
legal default is the bound itself. There is no shape of generic declaration
where a library cannot supply a default that satisfies its own bound.

The migration story for adding generics to existing classes is to declare
parameters with the bound as the default. Every existing subclass continues
to compile and run, while downstream code can specialize on its own
schedule.

Consequently, the deprecation-window proposal addresses a problem that
doesn't exist if defaults are used correctly. If a library declares
defaults equal to its bounds, missing arguments resolve to those defaults,
maintaining existing behavior. A deprecation window would only be necessary
if an author chooses not to provide sensible defaults.

I don't see a need to bake a deprecation window into the RFC. The existing
mechanism (using defaults equal to bounds), already covers the migration
path for zero-BC adoption.

Cheers,
Seifeddine

>

Reply via email to