Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:122688 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 qa.php.net (Postfix) with ESMTPS id E86921AD8F6 for ; Tue, 19 Mar 2024 16:38:19 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1710866320; bh=SuMpbDL3SuYbmaWlO2yXo5mewyN0K7ucdz/MV+FOPB4=; h=References:In-Reply-To:From:Date:Subject:To:Cc:From; b=cregv0NBNu3WNgTlsxea1ubTwszheYxw2h5dj0aLsFK6dTAEipeOwzqbGNA6DvODI PioDFyGwZ2aGrSCR8cNuYVSCudwWaTnZnnxhot8CZvV6TIe/gaDui6msSH5Y7/MTV+ mEtVaHnj+Z/6pFPiOjtjavNPBkiEb4jJsuuCTXHUOSB6J4jGl2w7ZdDJo/OeW31J4x x2r9n9q83WaFpSfboHqwDj8/SiPRe9qiAbDwPYvjU1mqtP4v9ZH15CXYaHXWqX9qBu XKJyBlR9FKlfHyutA1UcBplvWy6n3hENyaHx1JyjlvSUJQlsVZYqNL5CCgAoOrMmDU y8hrS7ppfbSIA== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 1A046180079 for ; Tue, 19 Mar 2024 16:38:39 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 4.0.0 (2022-12-13) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=-0.7 required=5.0 tests=BAYES_05,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_PASS,FREEMAIL_FROM, HTML_MESSAGE,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H3,RCVD_IN_MSPIKE_WL, SPF_HELO_NONE,SPF_PASS,T_SCC_BODY_TEXT_LINE autolearn=no autolearn_force=no version=4.0.0 X-Spam-Virus: No X-Envelope-From: Received: from mail-pf1-f179.google.com (mail-pf1-f179.google.com [209.85.210.179]) (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 ; Tue, 19 Mar 2024 16:38:35 +0000 (UTC) Received: by mail-pf1-f179.google.com with SMTP id d2e1a72fcca58-6e704078860so2872826b3a.0 for ; Tue, 19 Mar 2024 09:38:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1710866293; x=1711471093; darn=lists.php.net; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=9+w3ODbH/xACMN1Tgr3t1Be+czVAx9o6YkLca/nckx4=; b=czPQOxe3wto7sXtgpLTxPp21Hv7G18rfmMLZEUgjCELYiPEtDjD4agvVy+s4+K8PUF gwEb5eML6+ay8fVJtgGzJgtfcVgIDgMH4MW6EoP199C9cL9o7dmK7wTFdDO/31jN2ccl PLWVZPV9Uho6msxfqXIxcTgUuYHzmXNuTfV5Gu3SY1cgo2v3rfmud3Jg6z3OMdl0iZDK VZ0UzcYTzUT/Q+Fxwgk0Gt4vQlSCl39o27Rub7LMODvIXQfs1sYbBspecxirFucR/ShD N26QNshETpK6BX4NrhD1oTG1qcIqh3KCebOrVlFyzMx0Jz4zm940HV/2aZZhlBh3a69h +QUw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1710866293; x=1711471093; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=9+w3ODbH/xACMN1Tgr3t1Be+czVAx9o6YkLca/nckx4=; b=ATgOY3J1EfkAsE3OGD0+BoFqPvXNNS/qhpVFwjhp6ux/VKU3NFD5nsNUsmUhUxI3aT xC0RJW/wuhXGn9s8n8iMcOk/+PZ9ticyyeUWRUM8mo2JvKEYvYxMJJ8+rfT2723lmhjj 6Hpr54uHTNrgUHk/BOmRdlnFRZsrLVvQySh0UE7fs5h4aP4qZJyoByq605fhTKzZ1VAo +MRhb1d4ezvjn8f7LwBunp21n2Cb/n6K25eZC+trqVUTgS44x1YrkadQzgTjqQj7qai/ NKgK6L5lrxITaAhV2HY1VRQoVyJQT0MTVkCtMWDWsyRRs2pFfXYygyNtY5W+kLEiL7Cb eAgA== X-Gm-Message-State: AOJu0YyqIWSljjFES12IuCs1Q29hoz2UQ02brMVDi0C8vwMyLB1DBeAB udttG3ASGoz+MohqXtR/BZKBYC5H3QNiu6QeEaVNKjW9NbRwbiNQ2Nr+/Fju88+xkvmrbvx8hz6 QhXtBDRbr8r0hQ/O+LwIv3BdSdTo= X-Google-Smtp-Source: AGHT+IGUvNGnZWBb95gvu1To7MpCfe6lLDBqd8+XXb40G4UI/ZmSA2qpXPExOXM02W+cDtMcHMvmv6L2esx5SymAaLc= X-Received: by 2002:a05:6a21:3a96:b0:1a3:6a5a:540e with SMTP id zv22-20020a056a213a9600b001a36a5a540emr6691116pzb.55.1710866292372; Tue, 19 Mar 2024 09:38:12 -0700 (PDT) Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net MIME-Version: 1.0 References: In-Reply-To: Date: Tue, 19 Mar 2024 17:37:58 +0100 Message-ID: Subject: Re: [PHP-DEV] Proposal: AS assertions To: Robert Landers Cc: internals Content-Type: multipart/alternative; boundary="0000000000009dd76506140619bb" From: ocramius@gmail.com (Marco Pivetta) --0000000000009dd76506140619bb Content-Type: text/plain; charset="UTF-8" Hey Robert, On Tue, 19 Mar 2024 at 17:24, Robert Landers wrote: > Hello internals, > > I've been thinking about this as an RFC for awhile, but with generics > being far off (if at all), I'd like to propose a useful idea: reusing > the AS keyword in a different context. > > Example: > > $x = $attributeReflection->newInstance() as MyAttribute; > > This would essentially perform the following code: > > assert(($x = $attributeReflection->newInstance()) instanceof MyAttribute); > > but would work even if assertions are disabled, and would provide some > sanity when working with mixed return types, or even dealing with > interfaces where you want to be sure you are dealing with a concrete > type: > > class Query implements QueryInterface {} > > function getQuery(string $sql): QueryInterface {} > > $x = getQuery("select 1 = 1") as Query; > > which is more like: > > assert(($x = getQuery("select 1 = 1")) instanceof Query); > > It'd also be nice to have a non-throwing version where we simply > specify that the type is nullable: > > $x = $attributeReflection->newInstance() as ?MyAttribute; > if ($x === null) // do something since the attribute isn't MyAttribute > > which is more like: > > try { > assert(($x = $attributeReflection->newInstance()) instanceof > MyAttribute); > } catch { > $x = null > } > > Or a more complex type: > > $x = $attributeReflection->newInstance() as > PretttyAttribute|(UglyAttribute&UtilityAttribute); > > Essentially, by using "as", you can be 100% sure that the type is the > expected type signature, null (if the type signature includes null), > or an error to be thrown. > > Note that this isn't casting from one type to another, but asserting > that this type is the type you expect. It'd significantly help with > static analysis, IDE code completion, etc. > > What do you think? > > Robert Landers > Software Engineer > Utrecht NL > What's the advantage of a language construct over the following? ```php /** * @template T of object * @psalm-assert T $value * @param class-string $type */ function as(mixed $value, string $type): mixed { if (! $value instanceof $type) { throw SomeKindOfException::forMismatchingRequirements($value, $type); } return $value; } echo as(myExpression(), MyType::class)->methodOfMyType(); ``` See https://3v4l.org/iQPok See https://phpstan.org/r/708912d3-64e2-46f0-9f9e-467921a6489a See https://psalm.dev/r/7f30d63865 Note that `azjezz/psl` provides a very complete toolkit around this kind of tooling: https://github.com/azjezz/psl/tree/5f0aeacb708a33d5b2d53a832736c7767a99b215/src/Psl/Type One note: if what you are going for is what `azjezz/psl`, be aware that exception / error tracing design needs special attention here: it's not as simple as it looks! Marco Pivetta https://mastodon.social/@ocramius https://ocramius.github.io/ --0000000000009dd76506140619bb Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
Hey Robert,


On Tue, 19 Mar = 2024 at 17:24, Robert Landers <landers.robert@gmail.com> wrote:
Hello internals,

I've been thinking about this as an RFC for awhile, but with generics being far off (if at all), I'd like to propose a useful idea: reusing the AS keyword in a different context.

Example:

$x =3D $attributeReflection->newInstance() as MyAttribute;

This would essentially perform the following code:

assert(($x =3D $attributeReflection->newInstance()) instanceof MyAttribu= te);

but would work even if assertions are disabled, and would provide some
sanity when working with mixed return types, or even dealing with
interfaces where you want to be sure you are dealing with a concrete
type:

class Query implements QueryInterface {}

function getQuery(string $sql): QueryInterface {}

$x =3D getQuery("select 1 =3D 1") as Query;

which is more like:

assert(($x =3D getQuery("select 1 =3D 1")) instanceof Query);

It'd also be nice to have a non-throwing version where we simply
specify that the type is nullable:

$x =3D $attributeReflection->newInstance() as ?MyAttribute;
if ($x =3D=3D=3D null) // do something since the attribute isn't MyAttr= ibute

which is more like:

try {
=C2=A0 assert(($x =3D $attributeReflection->newInstance()) instanceof My= Attribute);
} catch {
=C2=A0 $x =3D null
}

Or a more complex type:

$x =3D $attributeReflection->newInstance() as
PretttyAttribute|(UglyAttribute&UtilityAttribute);

Essentially, by using "as", you can be 100% sure that the type is= the
expected type signature, null (if the type signature includes null),
or an error to be thrown.

Note that this isn't casting from one type to another, but asserting that this type is the type you expect. It'd significantly help with
static analysis, IDE code completion, etc.

What do you think?

Robert Landers
Software Engineer
Utrecht NL

What's the advantage of = a language construct over the following?

```php
/**
=C2=A0* @template T of object
=C2=A0* @= psalm-assert T $value
=C2=A0* @param class-string<T> $type<= /div>
=C2=A0*/
function as(mixed $value, string $type): m= ixed
{
=C2=A0=C2=A0=C2=A0 if (! $value instanceof $type= ) { throw SomeKindOfException::forMismatchingRequirements($value, $type); }=

=C2=A0 =C2=A0 return $value;
}

echo as(myExpression(), MyType::class)->methodOfMyT= ype();
```


Note that `azjezz/psl` provides a very complete toolkit around this kind = of tooling: https://github.com/azjezz/psl/tree/5f= 0aeacb708a33d5b2d53a832736c7767a99b215/src/Psl/Type

One note: if what you are going for is what `azjezz/psl`, be aware th= at exception / error tracing design needs special attention here: it's = not as simple as it looks!

--0000000000009dd76506140619bb--