Re: [PHP-DEV] Proposal: AS assertions
Hi Rowan On Tue, Mar 19, 2024 at 8:39 PM Rowan Tommins [IMSoP] wrote: > > As well pattern matching, which Ilija mentioned, another adjacent feature is > a richer set of casting operators. Currently, we can assert that something is > an int; or we can force it to be an int; but we can't easily say "make this > an int if safe, but throw otherwise" or "make this an int if safe, but > substitute null/$someValue otherwise". > > I've been considering how we can improve that for a while, but not settled on > a firm proposal - there's a lot of different versions we *could* support, so > choosing a minimal set is hard. I've thought about this in the context of pattern matching a while back. I was thinking about something like `$x is ~int`, where the pattern match is successful iff `$x` is coercible to `int` without loss of information. Given that patterns may be nested, `array<~int>` could check that all elements of an array are coercible to `int`. The same could work for literal patterns, e.g. `~5`, where `5`, `5.0` and `'5'` are all accepted. This can potentially be combined with the variable binding pattern, `$var @ pattern`. The syntax looks a bit confusing at first, but it basically makes sure that the matched value conforms to `pattern`, and then binds it to `$var`. Hence, something like `$foo as Foo { $bar @ ~int }` would 1. make sure `$foo` is an instance of `Foo`, 2. make sure `$foo->bar` is coercible to `int`, and then assigned the coerced value to `$bar`. (It gets more complicated, because the assignment must be delayed until the entire pattern matches.) If the pattern matching fails at any point, it throws. This is just an idea, neither the `as` operator nor the `~` pattern have been implemented. I don't know whether they are feasible. Anyway, we're probably going off-topic. :) Ilija
Re: [PHP-DEV] Proposal: AS assertions
On 19/03/2024 16:24, Robert Landers wrote: $x = $attributeReflection->newInstance() as ?MyAttribute; if ($x === null) // do something since the attribute isn't MyAttribute I think reusing nullability for this would be a mistake - ideally, the right-hand side should allow any type, so "$foo as ?Foo" should mean the same as "$foo as Foo|null". A better alternative might be to specify a default when the type didn't match: $x = $attributeReflection->newInstance() as ?MyAttribute else null; if ($x === null) // do something since the attribute isn't MyAttribute Which then also allows you to skip the if statement completely: $x = $attributeReflection->newInstance() as MyAttribute else MyAttribute::createDefault(); That then looks a lot like a limited-use version of syntax for catching an exception inline, which would be nice as a general feature (but I think maybe hard to implement?) $x = somethingThatThrows() catch $someDefaultValue; As well pattern matching, which Ilija mentioned, another adjacent feature is a richer set of casting operators. Currently, we can assert that something is an int; or we can force it to be an int; but we can't easily say "make this an int if safe, but throw otherwise" or "make this an int if safe, but substitute null/$someValue otherwise". I've been considering how we can improve that for a while, but not settled on a firm proposal - there's a lot of different versions we *could* support, so choosing a minimal set is hard. Regards, -- Rowan Tommins [IMSoP]
Re: [PHP-DEV] Proposal: AS assertions
Hi Marco On Tue, Mar 19, 2024 at 7:04 PM Marco Aurélio Deleu wrote: > > > On 19 Mar 2024, at 14:51, Ilija Tovilo wrote: > > > > Hi Robert > > > >> On Tue, Mar 19, 2024 at 5:24 PM Robert Landers > >> wrote: > >> > > See https://wiki.php.net/rfc/pattern-matching#throwing_alternative. I > > believe this idea would combine nicely with pattern matching. It has > > many more uses there than just simple class type matching, and could > > even be used for things like destructuring. > > That looks like a PHP dream. Has there been any work regarding that? https://github.com/iluuu1994/php-src/pull/102/files The implementation is mostly complete (it might slightly diverge from the current specification. Bob has called for a different implementation approach that might be more complex but potentially easier to optimize, I'll have to play around with it. There are also still some design decisions that we aren't completely sure about. For now, Larry and I are just trying to get property hooks over the finish line. Ilija
Re: [PHP-DEV] Proposal: AS assertions
Marco Deleu > On 19 Mar 2024, at 14:51, Ilija Tovilo wrote: > > Hi Robert > >> On Tue, Mar 19, 2024 at 5:24 PM Robert Landers >> wrote: >> >> 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); > > See https://wiki.php.net/rfc/pattern-matching#throwing_alternative. I > believe this idea would combine nicely with pattern matching. It has > many more uses there than just simple class type matching, and could > even be used for things like destructuring. > > Ilija That looks like a PHP dream. Has there been any work regarding that?
Re: [PHP-DEV] [RFC[ Property accessor hooks, take 2
On Tue, Mar 19, 2024, at 11:16 AM, Derick Rethans wrote: > On Fri, 8 Mar 2024, Larry Garfield wrote: > >> Hi folks. Based on earlier discussions, we've made a number of >> changes to the RFC that should address some of the concerns people >> raised. We also had some very fruitful discussions off-list with >> several developers from the Foundation, which led to what we feel are >> some solid improvements. >> >> https://wiki.php.net/rfc/property-hooks > > Some comments and questions: > > Be aware, the detection logic works on $this->[propertyName] directly > at > compile time, not on dynamic forms of it like $prop = 'beep'; > $this->$prop. That will not trigger a backing value. > > How can that not cause issues? For most uses that should be fine. But it's more that trying to do anything else would be vastly more complicated and error prone for the implementation. And most people hated the alternate $field variable from Kotlin. >The set hook's return type is unspecified, and will silently be > treated as void. > > What happens if you *do* specify a return type? Will it Error? Yes, it's a parse error. > Implicit ''set'' parameter > > If the write-type of a property is the same as its defined type > (this is the common case), then the argument may be omitted > entirely. > > … > > If the parameter is not specified, it defaults to $value. > > I am not a fan of this "magical" behaviour. Do we *need* this short cut, > and the following "Short-set"? We believe we do. One of the goals of the RFC is to reduce and avoid verbose boilerplate. The short-hands should be the common case, in practice. The only reason one would have to specify a set parameter is if there was a good reason to change the variable name or widen the set type. Both of those are a-typical situations. > With asymmetric visibility that was previously proposed, the > example can be further simplified. > > But it isn't here, so why is this example (and the next one) in the RFC? > :-) Many/most languages with accessors also have asymmetric visibility. Nikita's original RFC also combined them all into one. The two RFCs are not mutually dependent, but are mutually-supportive, by design. So this comes under the "why didn't we include feature X" heading, with an answer "that's in a separate RFC." > Interaction with constructor property promotion > > … In particular, the shorthand version of hook bodies and the > ability to call out to private methods if they get complicated > partially obviate the concern about syntactic complexity. > > Although that is true, it *does* add more complexity in tools that needs > to parse PHP, as there is now another piece of new syntax that needs to > be added (and tested with). That's true, but it's also inherently true of hooks themselves. And any other syntax improvement to the language. > ReflectionProperty has several new methods to work with hooks. > > getHooks(): array returns an array of \ReflectionMethod objects > keyed by the hook they are for. > > What will the name for the hook be? And shouldn't there be an enum > case for that as well? isn't its own hook. It's still a "get" hook, it just returns by reference. So it will be available with the "Get" enum value. If the return-by-ref status needs to be known for whatever reason, that is already available on the ReflectionMethod object that is returned. --Larry Garfield
Re: [PHP-DEV] Proposal: AS assertions
On Tue, 19 Mar 2024 at 17:46, Deleu wrote: > On Tue, Mar 19, 2024 at 1:42 PM Marco Pivetta wrote: > >> 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! >> > > I believe you answered your own question here. The proposal seems far > simpler and reaches 100% of PHP projects as opposed to the ones that either > opt to use psalm or opt to use azjezz/psl. > Eh, kinda: you'd need to check how `Psl\Type\TypeInterface` recursively validates types and throws meaningful errors. Having that in the engine, given its structure, is a massive BC surface that is best kept as a `composer` dependency that can move separately. I hardly see that working in a language-level RFC, with the speed at which the language can do BC incompatible changes. See https://github.com/azjezz/psl/blob/5f0aeacb708a33d5b2d53a832736c7767a99b215/src/Psl/Type/TypeInterface.php#L21-L35 See https://github.com/azjezz/psl/blob/5f0aeacb708a33d5b2d53a832736c7767a99b215/src/Psl/Type/Exception/CoercionException.php#L49 See https://github.com/azjezz/psl/blob/5f0aeacb708a33d5b2d53a832736c7767a99b215/src/Psl/Type/Exception/Exception.php#L22 See https://github.com/azjezz/psl/blob/5f0aeacb708a33d5b2d53a832736c7767a99b215/src/Psl/Type/Exception/TypeTrace.php That stuff is all but figured out, even in userland :-) Also worth mentioning: https://github.com/CuyZ/Valinor/blob/37993b64a6eb04dc0aee79e03f2ddb4f86ff9c3a/src/Mapper/TreeMapper.php#L23-L25 https://github.com/CuyZ/Valinor/blob/37993b64a6eb04dc0aee79e03f2ddb4f86ff9c3a/src/Mapper/MappingError.php#L13 and the whole rabbit hole behind that Marco Pivetta https://mastodon.social/@ocramius https://ocramius.github.io/
Re: [PHP-DEV] Proposal: AS assertions
Hi Robert On Tue, Mar 19, 2024 at 5:24 PM Robert Landers wrote: > > 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); See https://wiki.php.net/rfc/pattern-matching#throwing_alternative. I believe this idea would combine nicely with pattern matching. It has many more uses there than just simple class type matching, and could even be used for things like destructuring. Ilija
Re: [PHP-DEV] Proposal: AS assertions
On Tue, Mar 19, 2024 at 1:42 PM Marco Pivetta wrote: > 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! > I believe you answered your own question here. The proposal seems far simpler and reaches 100% of PHP projects as opposed to the ones that either opt to use psalm or opt to use azjezz/psl. -- Marco Deleu
Re: [PHP-DEV] Proposal: AS assertions
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); > > 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/
[PHP-DEV] Proposal: AS assertions
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); 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
Re: [PHP-DEV] Supporting object types in BCMath
Hi Jordan, > I've done a lot of performance tuning on my arbitrary precision library, and > will simply state for everyone here that I think the amount of development > effort involved in improving performance of the BCMath library is almost > certainly going to see a return on your effort that is not worth it. There > have been discussions over the last year on possibly working on bundling a > new arbitrary precision C library and providing something that is performant > enough to be generally useful in core, but that's not even at the RFC stage, > just investigations. > > I wouldn't say that improving BCMath is a waste of time, but there is also > probably lower hanging fruit once you get past things like Saki has here. > DevEx improvements. BCMath is slow, but currently no other extension can replace it in terms of computational accuracy. Therefore, I would like to advance these proposals with the aim of improving usability rather than improving performance. Since we have already completed the implementation of the important processes when creating the prototype, we will start preparing the RFC. Regards. Saki
Re: [PHP-DEV] [RFC[ Property accessor hooks, take 2
On Fri, 8 Mar 2024, Larry Garfield wrote: > Hi folks. Based on earlier discussions, we've made a number of > changes to the RFC that should address some of the concerns people > raised. We also had some very fruitful discussions off-list with > several developers from the Foundation, which led to what we feel are > some solid improvements. > > https://wiki.php.net/rfc/property-hooks Some comments and questions: Be aware, the detection logic works on $this->[propertyName] directly at compile time, not on dynamic forms of it like $prop = 'beep'; $this->$prop. That will not trigger a backing value. How can that not cause issues? The set hook's return type is unspecified, and will silently be treated as void. What happens if you *do* specify a return type? Will it Error? Implicit ''set'' parameter If the write-type of a property is the same as its defined type (this is the common case), then the argument may be omitted entirely. … If the parameter is not specified, it defaults to $value. I am not a fan of this "magical" behaviour. Do we *need* this short cut, and the following "Short-set"? With asymmetric visibility that was previously proposed, the example can be further simplified. But it isn't here, so why is this example (and the next one) in the RFC? :-) Interaction with constructor property promotion … In particular, the shorthand version of hook bodies and the ability to call out to private methods if they get complicated partially obviate the concern about syntactic complexity. Although that is true, it *does* add more complexity in tools that needs to parse PHP, as there is now another piece of new syntax that needs to be added (and tested with). ReflectionProperty has several new methods to work with hooks. getHooks(): array returns an array of \ReflectionMethod objects keyed by the hook they are for. What will the name for the hook be? And shouldn't there be an enum case for that as well? cheers, Derick