Hey Robert,

On Tue, 19 Mar 2024 at 17:24, Robert Landers <landers.rob...@gmail.com>
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&UtilityAttribute);
>
> 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<T> $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/

Reply via email to