Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:129703 X-Original-To: internals@lists.php.net Delivered-To: internals@lists.php.net Received: from php-smtp4.php.net (php-smtp4.php.net [45.112.84.5]) by lists.php.net (Postfix) with ESMTPS id BD6E01A00BC for ; Sat, 27 Dec 2025 21:06:44 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1766869609; bh=6Bltkc1yz90JGcR10gwyYEtAiOcmo0/eGn8I1nEJSrs=; h=References:In-Reply-To:From:Date:Subject:To:Cc:From; b=E9N1C1tDysh/gwXLEORTegRJBMOQuy8ZGC6KKDnOQ9wKYnhCumFLErj0C4SwZyNRZ qvnmz+2zFw9FyOmH24mXXJLbvGDVIDqcM68BZzdZeMWPCt0JpTMgbdbGpOYk3eYepj qo1orwkvGkRn1ttsyFBW1Z2FMcW52cYn6RCQueBy08GZ/9jAPdLK7CTgOUXFHOvUNG 2kBIV43efvKmRLb9d6x/tndu8zEgwHAjFoUe/t81G7CL1aUnfneHuBO184KkRroRiq U6Cvc/DVv6zckxbDbgXa7hBdMowm3V09aGyfxM7gy3LyygHImCvsTK5svQe9GBDk5a yjUvxBXOUFFKQ== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id ED12F18003E for ; Sat, 27 Dec 2025 21:06:47 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on php-smtp4.php.net X-Spam-Level: * X-Spam-Status: No, score=1.8 required=5.0 tests=BAYES_50,DMARC_NONE, FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM,HEADER_FROM_DIFFERENT_DOMAINS, HTML_MESSAGE,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE, SPF_PASS autolearn=no autolearn_force=no version=4.0.1 X-Spam-Virus: No X-Envelope-From: Received: from mail-oi1-f174.google.com (mail-oi1-f174.google.com [209.85.167.174]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Sat, 27 Dec 2025 21:06:37 +0000 (UTC) Received: by mail-oi1-f174.google.com with SMTP id 5614622812f47-4510974a6bbso2476545b6e.3 for ; Sat, 27 Dec 2025 13:06:32 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1766869592; x=1767474392; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=RkpLDYNKbQ9BSV7RyH6o/5XhJ7yDtzwX/F9ef+6rb20=; b=jZOLcmeu7xB3Xadg76RC2tGHwqdA0ern5zHIw993YRWEompt1eJL+4v6ZGLbChdQg0 46SJD3sUnt57dX55mI6rOxZJ4LQk6WF0YnzvUhldVQyS1K0Lwb4Eidvm4Cd0060x4ucr Z36pY8T9+MnVeXoLjfKmS47iCC++79v0xr0T5xqN2rb8x/AmFMdV8rZq3nFbiriMXSKA CqNcvqYIlb14KmL+vIB/fiDSG36Da+vd2EwJ0BCt1k+s3GsPAfZRQVnW9NOFZGb25C0M PVcJMCMia/rD4ljl0v3JQ17xChxeR3VB7J5uQiM+WSIC8uPPDmyWrz60V5oSG0/yWYUy MN+g== X-Gm-Message-State: AOJu0YxEjy0dFXWeT3JnOABEbnJ5tz8Ul5oyt3aIeblxlNcoxWgkA+rX 7ldxx7Suv7S9qZXm/CCG6QJKZ9wMsq9rj7gihcBQLEQr61pcHWhwXPWYzw/ZQ5KynYNH9Y/Mzvz PpaJnq55HNVtfOfsDaCXWq+ZcgMYuyyY= X-Gm-Gg: AY/fxX6xN+w+EmwpgsyUEXUbi5NILMml+BwnyZGVJbbrptzbhAeF44G/WkpEoTv1Qgi oKwFqqyRgQInHIWw8yF7TjZ1MW90eU0Dw9JVNbOtge/N1PghKlYkiMQ3+LEKTk4Zf6EJ2uOkc5s a3eo9wqItByhBizuwHeUQYfcChrXpcjQvz6gPBQv9EE4HiaxEmptZL3OJN9vPCUHqkphUJqdBNr b6zoblFP6Juerrwqe8SkVzTiKb7Y83lwxNegHzPbUa3uB8OWdGsHLnmg7nZ/AMpqJ6vtw== X-Google-Smtp-Source: AGHT+IFddKR6A8lHwfSSAgrMFczCZ62OvmlBZTDa/XWTI7O8cxpzCaeWPewQt1lT+2x5rM4HccOLS2A/79sIWAvgKuA= X-Received: by 2002:a05:6808:15a4:b0:43f:6979:6c9d with SMTP id 5614622812f47-457b2088330mr14985948b6e.7.1766869591833; Sat, 27 Dec 2025 13:06:31 -0800 (PST) Precedence: list list-help: list-unsubscribe: list-post: List-Id: x-ms-reactions: disallow MIME-Version: 1.0 References: <20251227152633.7EC311A00BD@lists.php.net> In-Reply-To: <20251227152633.7EC311A00BD@lists.php.net> Date: Sat, 27 Dec 2025 22:06:20 +0100 X-Gm-Features: AQt7F2rEPNkedu43r_rj8fJ8MhzeSWKXUch1yaiT9MmIaWC1WaqJGc2uPEPbkfU Message-ID: Subject: Re: [PHP-DEV] [RFC Idea] Generic Collection Notation for Iterables (array) To: Jordi Kroon Cc: internals@lists.php.net Content-Type: multipart/alternative; boundary="00000000000062bd760646f5623c" From: bukka@php.net (Jakub Zelenka) --00000000000062bd760646f5623c Content-Type: text/plain; charset="UTF-8" Hi, On Sat 27. 12. 2025 at 16:28, Jordi Kroon wrote: > Hi everyone, > > I would like to propose a new type in PHP: Generic Collection Notation > for Iterables (`array`). > > **The problem** > PHP provides several ways to represent sequences of values: > > - `array` > - `iterable` > - `Traversable` implementations > - `generators` > > While these constructs are fundamental to PHP, none of them allow the > developer to express **what type of values they yield or contain** at > the language level. > > > This leads to several issues: > - Static analysis relies heavily on PHPDoc annotations. > - Runtime type hints communicate structure but not intent. > - APIs lack self descriptive type information. > - Developers must duplicate type information across signatures and > documentation. > > **Example** > ```php > class UserService > { > /** > * @return User[] > */ > public function getAllUsers(): array > { > return [new User("Alice"), new User("Bob")]; > } > > /** > * @param User $user > * @param Role[] $roles > * @return void > */ > public function saveRoles($user, array $roles): void > { > // Some database call > } > } > ``` > > **The proposal** > Introduce parameterized iterable types using generic like syntax: > `array` > `iterable` > `Traversable` > `Generator` > > ```php > function processUsers(array $users): void { } > > function normalize(array $values): array { } > ``` > > Optionally we could also extend the new array syntax with this > functionality. > > ```php > function processUsers(User[] $users): void { } > > function normalize(string[] $values): string[] { } > ``` > > Key points: > - The existing `array` type remains valid and unchanged. > - `array` means an array whose elements are of type `T`. > - The syntax is currently invalid in PHP and therefore does not > introduce a backward compatibility break. > > **Example** > ```php > class UserService > { > public function getAllUsers(): array > { > return [new User("Alice"), new User("Bob")]; > } > > public function saveRoles(User $user, array $roles): void > { > // Some database call > } > > public function streamAllUsers(): Generator > { > yield new User("Alice"); > yield new User("Bob"); > } > > public function iterateAllUsers(): ArrayIterator > { > return new ArrayIterator([new User("Alice"), new User("Bob")]); > } > > public function getUserProvider(): Traversable > { > return new ArrayIterator([new User("Alice"), new User("Bob")]); > } > > public function normalizeNames(iterable $names): > iterable > { > foreach ($names as $name) { > yield trim($name); > } > } > } > ``` > > **Runtime semantics and enforcement** > The goal is that `array` and related iterable parameterizations > behave like other PHP types: if a value is passed or returned that > violates the declared type, a `TypeError` is thrown. > > The main question is when and how enforcement occurs for eager versus > lazy containers. > - Checked on function argument receive, property assignment, and return. > - Each element value is validated against `T`. > There are two approaches, but only one gives strong runtime guarantees. > > Preferred behavior: > - Validate on consumption when the iterable is iterated. > - If a yielded element violates `T`, throw `TypeError` at the point > of iteration. > > This mirrors the fact that `iterable` may be lazy and cannot be fully > validated up front. > > **Implementation** > I have basic C knowledge but I don't have much experience with Zend > Engine / PHP Core. However I am fully committed to: > - Write and maintain the RFC document. > - Test and write tests for the implementation or draft > - Participate in discussions > - Collaborate with anyone willing to help with the implementation > > **Keys and additional parameters** > This proposal focuses on element types. Keys are intentionally out of > scope for the initial version. > > Possible follow up work could explore `array` and > `Generator`. > > **Next steps** > - I would like to have hear any potential caveats. For example > regarding lazy loading. > - If the RFC gains internal support, I will officially publish the > first initial draft of the RFC. > - Whether supporting the shorthand `T[]` should be part of the initial > scope or a follow up > - Whether supporting `TKey` should be part of the initial scope or a > follow up This has been discussed in past and we actually talked about it internally quite recently. IMHO only T[] should be supported to not give partial generics and disallow nested arrays. Arnaud described best the possible issues in https://externals.io/message/125049#125069 . There is really not much point to do RFC without implementation so you will probably need to find someone who is willing and capable to implement it. Kind regards, Jakub > --00000000000062bd760646f5623c Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
Hi,<= /span>

On Sat 27. 12. 2025 at 16:28, Jordi Kroon <jordikroon@me.com> wrote:
Hi everyone,

I would like to propose a new type in PHP: Generic Collection Notation
for Iterables (`array<T>`).

**The problem**
PHP provides several ways to represent sequences of values:

- `array`
- `iterable`
- `Traversable` implementations
- `generators`

While these constructs are fundamental to PHP, none of them allow the
developer to express **what type of values they yield or contain** at
the language level.


This leads to several issues:
- Static analysis relies heavily on PHPDoc annotations.
- Runtime type hints communicate structure but not intent.
- APIs lack self descriptive type information.
- Developers must duplicate type information across signatures and
documentation.

**Example**
```php
class UserService
{
=C2=A0 =C2=A0 =C2=A0/**
=C2=A0 =C2=A0 =C2=A0 * @return User[]
=C2=A0 =C2=A0 =C2=A0 */
=C2=A0 =C2=A0 =C2=A0public function getAllUsers(): array
=C2=A0 =C2=A0 =C2=A0{
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return [new User("Alice"), new = User("Bob")];
=C2=A0 =C2=A0 =C2=A0}

=C2=A0 =C2=A0 =C2=A0/**
=C2=A0 =C2=A0 =C2=A0 * @param User $user
=C2=A0 =C2=A0 =C2=A0 * @param Role[] $roles
=C2=A0 =C2=A0 =C2=A0 * @return void
=C2=A0 =C2=A0 =C2=A0 */
=C2=A0 =C2=A0 =C2=A0public function saveRoles($user, array $roles): void =C2=A0 =C2=A0 =C2=A0{
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0// Some database call
=C2=A0 =C2=A0 =C2=A0}
}
```

**The proposal**
Introduce parameterized iterable types using generic like syntax:
`array<T>`
`iterable<T>`
`Traversable<T>`
`Generator<T>`

```php
function processUsers(array<User> $users): void { }

function normalize(array<string> $values): array<string> { } ```

Optionally we could also extend the new array syntax with this
functionality.

```php
function processUsers(User[] $users): void { }

function normalize(string[] $values): string[] { }
```

Key points:
=C2=A0 - The existing `array` type remains valid and unchanged.
=C2=A0 - `array<T>` means an array whose elements are of type `T`. =C2=A0 - The syntax is currently invalid in PHP and therefore does not
introduce a backward compatibility break.

**Example**
```php
class UserService
{
=C2=A0 =C2=A0 =C2=A0public function getAllUsers(): array<User>
=C2=A0 =C2=A0 =C2=A0{
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return [new User("Alice"), new = User("Bob")];
=C2=A0 =C2=A0 =C2=A0}

=C2=A0 =C2=A0 =C2=A0public function saveRoles(User $user, array<Role>= $roles): void
=C2=A0 =C2=A0 =C2=A0{
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0// Some database call
=C2=A0 =C2=A0 =C2=A0}

=C2=A0 =C2=A0 =C2=A0public function streamAllUsers(): Generator<User>=
=C2=A0 =C2=A0 =C2=A0{
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0yield new User("Alice");
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0yield new User("Bob");
=C2=A0 =C2=A0 =C2=A0}

=C2=A0 =C2=A0 =C2=A0public function iterateAllUsers(): ArrayIterator<Use= r>
=C2=A0 =C2=A0 =C2=A0{
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return new ArrayIterator([new User("= Alice"), new User("Bob")]);
=C2=A0 =C2=A0 =C2=A0}

=C2=A0 =C2=A0 =C2=A0public function getUserProvider(): Traversable<User&= gt;
=C2=A0 =C2=A0 =C2=A0{
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return new ArrayIterator([new User("= Alice"), new User("Bob")]);
=C2=A0 =C2=A0 =C2=A0}

=C2=A0 =C2=A0 =C2=A0public function normalizeNames(iterable<string> $= names):
iterable<string>
=C2=A0 =C2=A0 =C2=A0{
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0foreach ($names as $name) {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0yield trim($name);
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0}
=C2=A0 =C2=A0 =C2=A0}
}
```

**Runtime semantics and enforcement**
The goal is that=C2=A0 `array<T>`=C2=A0 and related iterable paramete= rizations
behave like other PHP types: if a value is passed or returned that
violates the declared type, a=C2=A0 `TypeError`=C2=A0 is thrown.

The main question is when and how enforcement occurs for eager versus
lazy containers.
-=C2=A0 =C2=A0Checked on function argument receive, property assignment, an= d return.
-=C2=A0 =C2=A0Each element value is validated against=C2=A0 `T`.
There are two approaches, but only one gives strong runtime guarantees.

Preferred behavior:
-=C2=A0 =C2=A0Validate on consumption when the iterable is iterated.
-=C2=A0 =C2=A0If a yielded element violates=C2=A0 `T`, throw=C2=A0 `TypeErr= or`=C2=A0 at the point
of iteration.

This mirrors the fact that=C2=A0 `iterable`=C2=A0 may be lazy and cannot be= fully
validated up front.

**Implementation**
I have basic C knowledge but I don't have much experience with Zend Engine / PHP Core. However I am fully committed to:
=C2=A0 - Write and maintain the RFC document.
=C2=A0 - Test and write tests for the implementation or draft
=C2=A0 - Participate in discussions
- Collaborate with anyone willing to help with the implementation

**Keys and additional parameters**
This proposal focuses on element types. Keys are intentionally out of
scope for the initial version.

Possible follow up work could explore=C2=A0 `array<TKey, TValue>`=C2= =A0 and
`Generator<TKey, TValue>`.

**Next steps**
=C2=A0 - I would like to have hear any potential caveats. For example
regarding lazy loading.
=C2=A0 - If the RFC gains internal support, I will officially publish the <= br> first initial draft of the RFC.
=C2=A0 - Whether supporting the shorthand `T[]` should be part of the initi= al
scope or a follow up
=C2=A0 - Whether supporting `TKey` should be part of the initial scope or a=
follow up

This ha= s been discussed in past and we actually talked about it internally quite r= ecently. IMHO only T[] should be supported=C2=A0to not give partial generic= s and disallow nested arrays.

Arnaud described best the possible issues in=C2=A0

=
There is really not much poin= t to do RFC without implementation so you will probably need to find someon= e who is willing and capable to implement it.

Kind regards,

Jakub
--00000000000062bd760646f5623c--