Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:122705 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 4446E1AD8F6 for ; Thu, 21 Mar 2024 11:41:47 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1711021328; bh=Qf263ptBG/t+pSCuYOY3MW3vGGVbyqRZ7QA47zE0EBw=; h=In-Reply-To:References:Date:From:To:Subject:From; b=R2FaffgiHjNYZXnTY8HigymA6jgccL+k1nSCGEy5rWoUZcXAlDP8GbqX+vJj6x6kJ iq59taioU6im8t5vkaBh7YqAmDq1Icx2MbvfqQv4zzYf8PpztuseIJ06ZaFqDKL8a6 bhmQpPwaALgCzatI25X4gNcoUzG6SUnx5ShlGE6hCvk+zLSHTwkUQgta/IMN0/EydH 3bZjnL3be8yjXp1a0iQBpRIazlS9dUirGCujElG7WO9OjxSzLrFZPlATL35rH5QpbS GjT7GYk6bOV/kx1dPbQWnmbmHUcBr+9e+PW4HG0SFLkW4f1OrpvoZs5Cy71kqP83mm nhMgaUQvV7PKA== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 1283718003F for ; Thu, 21 Mar 2024 11:42:07 +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.9 required=5.0 tests=BAYES_40,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_MISSING,HTML_MESSAGE, RCVD_IN_DNSWL_LOW,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 wfhigh7-smtp.messagingengine.com (wfhigh7-smtp.messagingengine.com [64.147.123.158]) (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 ; Thu, 21 Mar 2024 11:42:06 +0000 (UTC) Received: from compute6.internal (compute6.nyi.internal [10.202.2.47]) by mailfhigh.west.internal (Postfix) with ESMTP id 8EFBD18000BD for ; Thu, 21 Mar 2024 07:41:42 -0400 (EDT) Received: from imap50 ([10.202.2.100]) by compute6.internal (MEProxy); Thu, 21 Mar 2024 07:41:42 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=rwec.co.uk; h=cc :content-type:content-type:date:date:from:from:in-reply-to :in-reply-to:message-id:mime-version:references:reply-to:subject :subject:to:to; s=fm2; t=1711021302; x=1711107702; bh=sR6vOr7v6b MSzYFI+opJe4QbRanQi+Ap1yorGmiZGuU=; b=GA20XXSCoilMq5jd3R6j9pB530 mwprEhq1RwMbHcqiliE6/2gUwCggYo7YWHQy766y1dhc34P/tsWeAmHnGEQ3P39x TPcBym/jhQ+9KZHHXvHsNWlurPsU0lpWClZpy0a7wjAax3JZBE/jTm4771MTVbr8 RWz1h9/2ICbs4ibCaTayRlqJW9+rpL+P2vsb/6x2HG3S3fs00BwxiS7oaA5kvfiB daMw4U+bINHi68qnMVtWMS0t5+jB8DxO1RWssMzAisa4LucCpJnk2FHH+wcbhRIf TY70F7UY9IuazWsLPWWba6f53u86SESIbEKHrWRf8YrmA4N66BItcxwGLKNQ== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:content-type:content-type:date:date :feedback-id:feedback-id:from:from:in-reply-to:in-reply-to :message-id:mime-version:references:reply-to:subject:subject:to :to:x-me-proxy:x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s= fm2; t=1711021302; x=1711107702; bh=sR6vOr7v6bMSzYFI+opJe4QbRanQ i+Ap1yorGmiZGuU=; b=ehwMJLxzRfdW1Dk9nE+vEEqbOaoevurLgA8ETtYukVmB ABcUDdkKNhYM+t2vj6jb8yWG8zkwq5NlTMI00mPVa0CloCYZKaxL9YTgpRZnIKrX v6H80WzK2FW1LRUv8yn4fnLAHT6+s50Utm6ofUwx1P6UCutY4odyulSsPlvuZTtc FAGXdHs+aJf3967fzN0XM5/4ioUgbm/skHGolQDwEUyPkEJZhleKLNGPNcFkvpnv OE49M3zLiMwdESmUj/OiDJX3lxt/JUR0UxjSaVFDZ6Xxkhhql4s1zdMQwjJHFNcB H39Bs/OiWAd7mYWyQ6NyNzGuLDjQnoBM/jzoHHtMhw== X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedvledrleeigdefudcutefuodetggdotefrodftvf curfhrohhfihhlvgemucfhrghsthforghilhdpqfgfvfdpuffrtefokffrpgfnqfghnecu uegrihhlohhuthemuceftddtnecunecujfgurhepofgfggfkjghffffhvffutgesrgdtre erreertdenucfhrhhomhepfdftohifrghnucfvohhmmhhinhhsucglkffoufhorfgnfdcu oehimhhsohhprdhphhhpsehrfigvtgdrtghordhukheqnecuggftrfgrthhtvghrnhepje fgffelffduhedvgfefhfekffegtdegledtkeelveehvefgleejgedtgeeiiedunecuvehl uhhsthgvrhfuihiivgeptdenucfrrghrrghmpehmrghilhhfrhhomhepihhmshhophdrph hhphesrhifvggtrdgtohdruhhk X-ME-Proxy: Feedback-ID: id5114917:Fastmail Received: by mailuser.nyi.internal (Postfix, from userid 501) id C49B11700093; Thu, 21 Mar 2024 07:41:41 -0400 (EDT) X-Mailer: MessagingEngine.com Webmail Interface User-Agent: Cyrus-JMAP/3.11.0-alpha0-332-gdeb4194079-fm-20240319.002-gdeb41940 Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net MIME-Version: 1.0 Message-ID: <2d7ec203-6e80-445c-94f4-d29ef58743b1@rwec.co.uk> In-Reply-To: References: <3F78A125-1946-42E2-A4F5-A2B282BE2107@rwec.co.uk> Date: Thu, 21 Mar 2024 11:41:19 +0000 To: internals@lists.php.net Subject: Re: [PHP-DEV] Proposal: AS assertions Content-Type: multipart/alternative; boundary=fefae9dbcc694ba385d3d3e8738bd6e6 From: imsop.php@rwec.co.uk ("Rowan Tommins [IMSoP]") --fefae9dbcc694ba385d3d3e8738bd6e6 Content-Type: text/plain On 20/03/2024 23:05, Robert Landers wrote: > In other > words, I can't think of a case where you'd actually want a Type|null > and you wouldn't have to check for null anyway. It's not about having to check for null; it's about being able to distinguish between "a null value, which was one of the expected types" and "a value of an unexpected type". That's a distinction which is made everywhere else in the language: parameter types, return types, property types, will all throw an error if you pass a Foo when a ?Bar was expected, they won't silently coerce it to null. > If you think about it, in this proposal, you could use it in a match: > > // $a is TypeA|TypeB|null > > match (true) { > $a as ?TypeA => 'a', > $a as ?TypeB => 'b', > $a === null => 'null', > } That won't work, because match performs a strict comparison, and the as expression won't return a boolean true. You would have to do this: match (true) { (bool)($a as ?TypeA) => 'a', (bool)($a as ?TypeB) => 'b', $a === null => 'null', } Or this: match (true) { ($a as ?TypeA) !== null => 'a', ($a as ?TypeB) !== null => 'b', $a === null => 'null', } Neither of which is particularly readable. What you're really looking for in that case is an "is" operator: match (true) { $a is TypeA => 'a', $a is TypeB => 'b', $a === null => 'null', } Which in the draft pattern matching RFC Ilija linked to can be abbreviated to: match ($a) is { TypeA => 'a', TypeB => 'b', null => 'null', } Of course, in simple cases, you can use "instanceof" in place of "is" already: match (true) { $a instanceof TypeA => 'a', $a instanceof TypeB => 'b', $a === null => 'null', } > Including `null` in that type > seems to be that you would get null if no other type matches, since > any variable can be `null`. > I can't think of any sense in which "any variable can be null" that is not true of any other type you might put in the union. We could interpret Foo|false as meaning "use false as the fallback"; or Foo|int as "use zero as the fallback"; but I don't think that would be sensible. In other words, the "or null on failure" part is an option to the "as" expression, it's not part of the type you're checking against. If we only wanted to support "null on failure", we could have a different keyword, like "?as": $bar = new Bar; $bar as ?Foo; // Error $bar ?as Foo; // null (as fallback) $null = null; $null as ?Foo; // null (because it's an accepted value) $null ?as Foo; // null (as fallback) A similar suggestion was made in a previous discussion around nullable casts - to distinguish between (?int)$foo as "cast to nullable int" and (int?)$foo as "cast to int, with null on error". Note however that combining ?as with ?? is not enough to support "chosen value on failure": $bar = new Bar; $bar ?as ?Foo ?? Foo::createDefault(); // creates default object $null = null; $null ?as ?Foo ?? Foo::createDefault(); // also creates default object, even though null is an expected value That's why my earlier suggestion was to specify the fallback explicitly: $bar = new Bar; $bar as ?Foo else null; // null $bar as ?Foo else Foo::createDefault(); // default object $null = null; $nulll as ?Foo else null; // null $null as ?Foo else Foo::createDefault(); // also null, because it's an accepted value, so the fallback is not evaluated Probably, it should then be an error if the fallback value doesn't meet the constraint: $bar = new Bar; $bar as Foo else null; // error: fallback value null is not of type Foo $bar as ?Foo else 42; // error: fallback value 42 is not of type ?Foo Regards, -- Rowan Tommins [IMSoP] --fefae9dbcc694ba385d3d3e8738bd6e6 Content-Type: text/html Content-Transfer-Encoding: quoted-printable
On 20/03/2024 23:05, Robert Landers wrote:
In other
words, I can't think of a case where you'd actually want a Type|null
and you wouldn't have to check for null anyway.

It's not about having to check for null; it's about being ab= le to distinguish between "a null value, which was one of the expected types" and "a value of an unexpected type".

That's a dis= tinction which is made everywhere else in the language: parameter types, return types, property types, will all throw an error if you pass a Foo when a ?Bar was expected, they won't silently coerce it to null.



If you thi=
nk about it, in this proposal, you could use it in a match:

// $a is TypeA|TypeB|null

match (true) {
  $a as ?TypeA =3D> 'a',
  $a as ?TypeB =3D> 'b',
  $a =3D=3D=3D null =3D> 'null',
}


That won't work, because match per= forms a strict comparison, and the as expression won't return a boolean true. You would have to do this:

match (true) {
  (bool)($a as ?TypeA) =3D> 'a',
  (bool)($a as ?TypeB) =3D> 'b',
  $a =3D=3D=3D null =3D> 'null',
}

Or this:

match (true) {
  (= $a as ?TypeA) !=3D=3D null =3D> 'a',
  ($a as ?Typ= eB) !=3D=3D null =3D> 'b',
  $a =3D=3D=3D null =3D= > 'null',
}


Neither of which is particularly readable. What you're really looking for in that case is an "is" operator:
match (true) {
  $a is TypeA =3D> 'a',
  $a is TypeB =3D> 'b',
  $a =3D=3D=3D null =3D> 'null',
}

Which in the draft pattern matching RFC Ilija linked to ca= n be abbreviated to:

match ($a) is {
  TypeA =3D> 'a',
  TypeB =3D> 'b',
  null =3D> 'null',
}


Of course, in simple cases, you can use "instan= ceof" in place of "is" already:

match (true) {
  $a instanceof TypeA =3D> 'a',
  $a instanceof TypeB =3D> 'b',
  $a =3D=3D=3D null =3D> 'null',
}



Including `null` in that type seems to be that you would get null if no other type matches, since any variable can be `null`.


I can't think of any sense in whi= ch "any variable can be null" that is not true of any other type you mig= ht put in the union. We could interpret Foo|false as meaning "use false = as the fallback"; or Foo|int as "use zero as the fallback"; but I don't = think that would be sensible.

In other words, the "or null o= n failure" part is an option to the "as" expression, it's not part of the type you're checking against. If we only wanted to support "null on failure", we could = have a different keyword, like "?as":

$bar =3D new Bar;
<= /div>
$bar as ?Foo; // Error
$bar ?as Foo; // nul= l (as fallback)

$null =3D null;
$null as ?Foo; // null (because it's an accepted value)
$null ?as Foo; // null (as fallback)

A sim= ilar suggestion was made in a previous discussion around nullable casts = - to distinguish between (?int)$foo as "cast to nullable int" and (int?)= $foo as "cast to int, with null on error".

=
Note however that combining ?as with ?? is not enough to = support "chosen value on failure":

$b= ar =3D new Bar;
$bar ?as ?Foo ?? Foo::createDefault()= ; // creates default object

$null =3D null;=
$null ?as ?Foo ?? Foo::createDefault(); // also creates d= efault object, even though null is an expected value

<= /div>
That's why my earlier suggestion was to specify the fallback e= xplicitly:

$bar =3D new Bar;
$bar as ?Foo else null; // null
$bar as ?Foo else Fo= o::createDefault(); // default object

$null= =3D null;
$nulll as ?Foo else null; // null
$null as ?Foo else Foo::createDefault(); // also null, because it'= s an accepted value, so the fallback is not evaluated

=
Probably, it should then be an error if the fallback value do= esn't meet the constraint:

$bar =3D n= ew Bar;
$bar as Foo else null; // error: fallback value nu= ll is not of type Foo
$bar as ?Foo else 42; // error: fall= back value 42 is not of type ?Foo


=

Regards,
--
Rowan Tommins
[IMSoP]
--fefae9dbcc694ba385d3d3e8738bd6e6--