Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:122720 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 6EF061A009C for ; Fri, 22 Mar 2024 00:04:42 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1711065903; bh=f2LqPe12j/gcoNL+7P5ynpZh8/B5xU1Fmgf4yfG1uTA=; h=References:In-Reply-To:From:Date:Subject:To:Cc:From; b=WGaIZAQ2LtMfsjAo15SfoAseiO3RI+ngQP8JR4Pp+0ya/gZmoQp82IrQ66SGQbzdV eaRL15tD/s2i+/r+alFlsp7Xpv2njqxfNKTHmy6dPomIvYlleKlz3vtPa63Ce32Hs1 1Qn5u2C4/QPZrNH5ejqusLgDVdgwFVW4XUA9g1W7qb+owztfyjgqTBA1yiojMbKBSl BrjT+LpuXCINV9HCbG+9b//vmX/7KKI7EGE+Yk5lLFkRTnw1DRj6cHM+TeqEXBzNsA +S3gfh/PVgxlUh4Q7iaS69YxDBJQwXo9/Ehi/AcqJ8+MrBLNjOMAHPtX0fn6C4MoEF brop0k79BmyIQ== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 282EF180041 for ; Fri, 22 Mar 2024 00:05:03 +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.6 required=5.0 tests=BAYES_50,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_PASS,FREEMAIL_FROM, 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-oo1-f49.google.com (mail-oo1-f49.google.com [209.85.161.49]) (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 ; Fri, 22 Mar 2024 00:05:02 +0000 (UTC) Received: by mail-oo1-f49.google.com with SMTP id 006d021491bc7-5a4f608432bso885377eaf.2 for ; Thu, 21 Mar 2024 17:04:40 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1711065879; x=1711670679; darn=lists.php.net; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:from:to:cc:subject:date :message-id:reply-to; bh=Dtd5sD1vL4J0KArSHoG3xhZJKLIYeN9IqNRB/lntqAk=; b=KV6Ai2V3Djfc6MUuBCAxBtx9Ws67krEW7T1AyiEg1dpcaG2qSkNlu7wBV6jnCDy1yx MOGStkpqYZqi5W/dT/o6QwYNLruQFIu2LyIA0tai1PnGvhYM5rMWfkZgnezcu8ueZrcr cUpep/7Qgk8Klf9Vi7fbnEhKB8Gh9owykClxjGjzcQIX/dbtxkbVkM5otx5dGOhGAkRA ES+Uabubrj0ZpHsdRoQ458IfGuQfpuzyFKtMxP85DT9Wzg4mTiz7bfCUb2LXw/ubal84 I43FzPl2zSKgdjasqdbxdLERnmYCOL0Azsf+mAYp3UywIZn8SmbPWQ6t9vXMeMv+H3GL FaRQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1711065879; x=1711670679; h=content-transfer-encoding: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=Dtd5sD1vL4J0KArSHoG3xhZJKLIYeN9IqNRB/lntqAk=; b=mQf26WvmAwnZInzJfTwC/TnGPYIEPXazgrh1Q33seQaQfoZXwj+WIAebQCUKk0SYHw z6kh3W1JNhMWz7roa33JFsrzE3gVvaoyq0rCtHQzvxzGg7XAqqyl4fPJLTMlslpMcOFI 5O9LwoVG0Nsij4+Ut79mw9oWIxEwlQCLf2t84pQC8YFsS8L09OPuS0rPl7XPPlxCpV1v LTF/MSmoqLLqgp6pSUpYN+eIkK/N3S1FqJlUo7LMJDl2ighxuxUf5Mlv0CW8YxUcP2Gh cB7oIb8Y81f+eXqC/q2HaVKyXZBE+wDMigFa4ENPIo6fhcLgBTZ0Qd82nx1HWKtxHpyt lJjg== X-Gm-Message-State: AOJu0YzkR5v3ufNB+G8XwwLiSwUI/K8FcGMA8LhQ/Fmz/zeLl/NUkyzt 4cqrcbvJTP4scJVKBGXzjiH2Mqk7M1ydfiOGizuHaBSxLowL9WWN2O7IqjilRQki0dpcnCSzmRO ybvny8OOfPSiwgHXaNxb0/0NPX7yXyolKxyveIA== X-Google-Smtp-Source: AGHT+IHIA1+hsL5QATIlM7kyfyH/5e1yxBHKcm990LZKUNaqhlyGxXpzlmJV2umRnAD1G9enHxHgSRranw8BNmF7uoM= X-Received: by 2002:a05:6820:215:b0:5a1:b571:4871 with SMTP id bw21-20020a056820021500b005a1b5714871mr1185367oob.5.1711065879583; Thu, 21 Mar 2024 17:04:39 -0700 (PDT) Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net MIME-Version: 1.0 References: <3F78A125-1946-42E2-A4F5-A2B282BE2107@rwec.co.uk> <2d7ec203-6e80-445c-94f4-d29ef58743b1@rwec.co.uk> <40d5c22f-5c06-403d-afc2-84c202277a29@rwec.co.uk> In-Reply-To: Date: Fri, 22 Mar 2024 01:04:27 +0100 Message-ID: Subject: Re: [PHP-DEV] Proposal: AS assertions To: "Rowan Tommins [IMSoP]" Cc: internals@lists.php.net Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable From: landers.robert@gmail.com (Robert Landers) On Thu, Mar 21, 2024 at 11:06=E2=80=AFPM Rowan Tommins [IMSoP] wrote: > > On 21/03/2024 19:03, Robert Landers wrote: > > I suppose we are taking this from different viewpoints, yours appears > to be more of a philosophical one, whereas mine is more of a practical > one. > > > My main concern is consistency; which is partly philosophical, but does h= ave practical impact - the same syntax meaning the same thing in different = contexts leads to less user confusion and fewer bugs. > > But I also think there are real use cases for "error on anything other th= an either Foo or null" separate from "give me a null for anything other tha= n Foo". I can't think of any example of this. In every case I've ever written a manual typecheck, I've done something differently from null vs. the actual type I'm checking for. In only a few instances were they ever the same. I'd be happy to research this by looking at some older code that has to do manual typechecks; but I have a feeling if you were to make Foo|null only throw, it would be pointless as most people would end up writing this anyway, making the |null completely superfluous: if $x =3D=3D=3D null { /* do something for null */ } $y =3D $x as Foo; > $x =3D $a as null; > > (or any other value, such as true|false) appears to have no practical > purpose in this particular case. > > > There's plenty of possible pieces of code that have no practical purpose,= but that on its own isn't a good reason to make them do something differen= t. > > "null" as a standalone type (rather than part of a union) is pretty much = always pointless, and was forbidden until PHP 8.2. It's now allowed, partly= because there are scenarios involving inheritance where it does actually m= ake sense (e.g. narrowing a return type from Foo|null to null); and probabl= y also because it's easier to allow it than forbid it. > > > That's not really what we're talking about anyway, though; we're talking = about nullable types, or null in a union type, which are much more frequent= ly used. I think that is where we are getting confused: `null` is a value (or at least, the absence of a value). The fact that the type system allows it to be used as though its a type (along with true and false) is interesting, but I think it is confusing the conversation. It might be worth defining the meaning of "type" and "value" as well defining what the "as" means in each context. Is it the same? Is it different? I think this is worth spending some time on, but I have a feeling there'll be bigger discussion about that when pattern matching shows up. > Further, reading "$x =3D > $a as null", as a native English speaker, appears to be the same as > "$x =3D null". > > > Well, that's a potential problem with the choice of syntax: "$x =3D $a as= int" could easily be mistaken for "cast $a as int", rather than "assert th= at $a is int". > > If you spell out "assert that $a is null", or "assert that $a is int|null= ", it becomes very surprising for 'hello' to do anything other than fail th= e assertion. $a as int is quite different from $a as null. One is a bonafide type, the other is a value. I don't think you can mistake this and they are very different semantics. > As I mentioned in the beginning, I see this mostly being used when > dealing with mixed types from built-in/library functions, where you > have no idea what the actual type is, but when you write the code, you > have a reasonable expectation of a set of types and you want to throw > if it is unexpected. > > > My argument is that you might have a set of expected types which includes= null, *and* want to throw for other, unexpected, values. If "|null" is spe= cial-cased to mean "default to null", there's no way to do that. I'm arguing that that doesn't make any sense; this isn't a method/function signature. I invite you to try writing some fictional code using both semantics. I'm being sincere when I say I'd love to see an example that would use |null on the right hand side of "as" and want to throw. > Right now, the best way to do that is to simply > set a function signature and pass the mixed type to the function to > have the engine do it for you > > > And if you do that, then a value of 'hello' passed to a parameter of type= int|null, will throw a TypeError, not give you a null. > > As I illustrated in my last e-mail, you can even (since PHP 8.2) have a p= arameter of type null, and get a TypeError for any other value. That may no= t be useful, but it's entirely logical. Ah, yeah, I guess I could have been more clear. This is what I find myself writing quite a lot of lately (unfortunately): foreach ($listOfMyAttributes as $attributeReflection) { $instance =3D $attributeReflection->newInstance(); assert($instance instanceof MyAttribute); $instance->someMethod(); } as well as: $value =3D genericFuncReturnsMixed(); if ($value =3D=3D=3D null) { /* handle null */ } if($value instanceof MyType) { $value->doSomething(); } else if ($value instanceof MyOtherType) { $value->doSomethingDifferent(); } This would be better written as foreach ($listOfMyAttributes as $attributeReflection) { $instance =3D $attributeReflection->newInstance() as MyAttribute; } or $value =3D genericFuncReturnsMixed(); if ($value =3D=3D=3D null) { /* handle null */ } ($value as MyType|null)?->doSomething(); ($value as MyOtherType|null)?->doSomethingDifferent(); Not having an "escape hatch" would mean writing something like: $value =3D genericFuncReturnsMixed(); if ($value =3D=3D=3D null) { /* handle null */ } try { ($value as MyType)->doSomething(); } catch { } try { ($value as MyOtherType|null)->doSomethingDifferent(); } catch { } which will probably trip up a bunch of lint rules for having an empty catch ... but that is beside the point. > It makes more sense, from a practical programming > point-of-view, to simply return the value given if none of the types > match. > > > This perhaps is a key part of our difference: when I see "int|bool|null",= I don't see any "value given", just three built-in types: int, which has a= range of values from PHP_INT_MIN to PHP_INT_MAX; bool, which has two possi= ble values "true" and "false"; and null, which has a single possible value = "null". > > So there are 2**64 + 2 + 1 possible values that meet the constraint, and = nothing to specify that one of those is my preferred default if given somet= hing unexpected. I agree this is the key point, and goes back to what I was saying about null not actually being a type, but a value. If any value could be a type, I think I would feel differently, but I'd still argue that null should be a special case for the nice syntax you get from that. > > Regards, > > -- > Rowan Tommins > [IMSoP]