Re: [PHP-DEV] Proposal: AS assertions

2024-03-19 Thread Ilija Tovilo
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

2024-03-19 Thread Rowan Tommins [IMSoP]

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

2024-03-19 Thread Ilija Tovilo
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

2024-03-19 Thread Marco Aurélio Deleu


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

2024-03-19 Thread Larry Garfield
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

2024-03-19 Thread Marco Pivetta
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

2024-03-19 Thread Ilija Tovilo
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

2024-03-19 Thread Deleu
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

2024-03-19 Thread Marco Pivetta
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

2024-03-19 Thread Robert Landers
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

2024-03-19 Thread Saki Takamachi
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

2024-03-19 Thread Derick Rethans
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