Re: Blog post: If we can get partial generics, should we?
| From: | Rob Landers | Date: | Tue, 05 Aug 2025 06:39:07 +0000 |
| Subject: | Re: Blog post: If we can get partial generics, should we? | ||
| References: | 1 2 3 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-128393@lists.php.net to get a copy of this message | ||
On Tue, Aug 5, 2025, at 08:19, Rob Landers wrote:
>
> On Mon, Aug 4, 2025, at 22:49, Levi Morrison wrote:
>> On Mon, Aug 4, 2025 at 9:26 AM Larry Garfield <larry@garfieldtech.com> wrote:
>> >
>> > Hi folks. I recently wrote a blog post on behalf of the PHP Foundation about some
>> > work Gina has been doing regarding generics. It's a follow-up to last year's post on a
>> > similar topic. We are especially looking for feedback from the Internals crew, and voters in
>> > particular, on whether or not to proceed.
>> >
>> >
>> > https://thephp.foundation/blog/2025/08/05/compile-generics/
>> >
>> > Wearing my Internals/voter hat, my stance is a very strong "yes please!"
>> >
>> > --
>> > Larry Garfield
>> > larry@garfieldtech.com
>>
>> I played with a few iterations of Gina's work. My opinion at that time
>> was that there are too many restrictions to be very useful. Based on
>> those evaluations, I would not consider it to be "80% of the
>> benefit.". With that said, maybe Gina has removed some of the
>> restrictions. Based on the article it , but I'll commit to trying it
>> out again.
>>
>> One thing the article says we're missing and that we we definitely
>> need is unions, or at the very least the union with null. Nullable
>> types are everywhere, and even in your interfaces it's going to show
>> up. For instance, if we had interfaces for Set, Map, etc, there are
>> going to be operations which return nullable values. Sure, exceptions
>> can be thrown in some cases but not all of them. For instance, if you
>> are building a caching data structure, throwing an exceptions on a
>> cache miss would be very bad for performance*. You can separate it
>> into has + get, but this has two lookups which is not desirable for
>> performance. You can return a "tuple" like
[found,
>> $value] where
>> $value may be null, but this loses even more type safety,
>> and isn't
>> a very common pattern in PHP. You really want T|null.
>>
>> There are probably other "edges" that are important and should be
>> handled in this first pass, but missing nullable types is not a small
>> thing.
>>
>> * We'll get a mini-optimization in PHP 8.5 for making the exception
>> object faster to construct, but it doesn't speed up the backtrace,
>> which is where most of the performance cost comes from in practice. If
>> you are benchmarking this, make sure to include cases where your stack
>> is a few dozen frames deep. I work for an observability company for my
>> day job, and it is insanely normal for there to be 10+ frames, and
>> still fairly common to be 30+ frames. Think about routing, frameworks,
>> middleware, helper functions, libraries and abstractions, etc. If you
>> use middleware in particular, expect 50+ frames to be normal.
>>
>
> I have a few different ideas for unions/intersections, but to implement them now would be
> refactoring for the sake of refactoring. There would be no benefit to implement any of it now.
>
> Earlier this year I tried interning types, so that type checking could be simple pointer
> equality. From there, I implemented a tri, such that intersections and unions were very fast to
> check.
>
> The result is that it was virtually useless with today’s php, except for the most
> pathological cases. PHP doesn’t do much type checking — currently.
>
> In any case, the changes required to pull it off (not counting opcache) were huge. If we were
> to run into the pathological cases more often (such as with runtime generics), it might be worth it.
>
> All that being said, we are talking about *compile-time* generics. If there is a time to be
> less efficient, it is during compilation, which generally happens once (esp with opcache). Further,
> pathological type checking is quite rare in codebases (most types are relatively simple without deep
> hierarchical intersections and unions), so I assume it will be fine to enable them.
>
> — Rob
It’s also worth pointing out that nullable or a union of null and another type is technically just
one type with the nullable bit set. So, I suspect nullable types will still obey the “single
type” rules proposed here.
— Rob