Re: Proposal: AS assertions

From: Date: Fri, 22 Mar 2024 18:47:45 +0000
Subject: Re: Proposal: AS assertions
References: 1 2 3 4 5 6 7 8 9 10 11 12  Groups: php.internals 
Request: Send a blank email to internals+get-122732@lists.php.net to get a copy of this message
On Fri, Mar 22, 2024 at 5:51 PM Rowan Tommins [IMSoP] <imsop.php@rwec.co.uk> wrote: > > On Fri, 22 Mar 2024, at 12:58, Robert Landers wrote: > > > >> $optionalExpiryDateTime = $expiry as ?DateTimeInterface else new > >> DateTimeImmutable($expiry); > > I'm not sure I can grok what this does... > > > > $optionalExpiryDateTime = ($expiry === null || $expiry instanceof > > DateTimeInterface) ? $expiry : new DateTimeImmutable($expiry) > > Trying to write it as a one-liner is going to make for ugly code - that's why I'd > love to have a new way to write it! But yes, that's the right logic. > > With the "is" operator from the Pattern Matching draft, it would be: > > $optionalExpiryDateTime = ($expiry is ?DateTimeInterface) ? $expiry : new > DateTimeImmutable($expiry); > > But with a clearer assertion that the variable will end up with the right type in all cases: > > $optionalExpiryDateTime = $expiry as ?DateTimeInterface else some_other_function($expiry); > assert($optionalExpiryDateTime is ?DateTimeInterface); // cannot fail, already asserted by the > "as" > > > > Maybe? What would be the usefulness of this in real life code? I've > > never written anything like it in my life. > > I already explained the scenario: the parameter is optional, so you want to preserve nulls; but > if it *is* present, you want to make sure it's the correct type before proceeding. Another > example: > // some library function that only supports strings and nulls > function bar(?string $name) { > if ( $string !== null ) ... > else ... > } > // a function you're writing that supports various alternative formats > function foo(string|Stringable|int|null $name = null) { > // we don't want to do anything special with nulls here, just pass them along > // but we do want to convert other types to string, so that bar() doesn't reject them > bar($name as ?string else (string)$name); > } This breaks my brain; in a good way I think. As you pointed out, people could now write this: function (int|string|null $value) { foo($value as int|null else (int)$value); } Which would pass int or null down to foo. Especially because I see something like this too often (especially with strict types): function (int|string|null $value) { foo((int) $value); } And foo() gets a 0 when $value is null and "undefined" things start happening. This isn't really possible with any of the other syntaxes I proposed. Now, if we are dealing with function returns: ($x as MyType else null)?->doSomething(); I don't hate it. It's a bit wordy, but still better than the alternative. > > To put it another way, it's no different from any other union type: at some point, you > will probably want to handle the different types separately, but at this point in the program, > either type is fine. In this case, the types that are fine are DateTimeInterface and null; or in the > example above, string and null. > > > > $optionalExpiryDateTime = $expiry == null ? $expiry : $expiry as > > DateTimeInterface ?? new DateTimeImmutable($expiry as string ?? "now") > > If you think that's "readable" then we might as well end the conversation here. > If that was presented to me in a code review, I'd probably just write "WTF?!" Hahaha, yeah, I wrote that before reading my own example! > > I have no idea looking at that what type I can assume for $optionalExpiryDateTime after that > line, which was surely the whole point of using "as" in the first place? > > Regards, > -- > Rowan Tommins > [IMSoP]

« previous php.internals (#122732) next »