Re: [PHP-DEV] Re: [Pre-RFC] Scalar object methods (with a working implementation)

Seifeddine Gmati <[email protected]>
Newsgroups gmane.comp.php.devel
Message-ID <CAFyg4HhoU6QZ41gFxG1+4=KJAvKCGOHbdUDtSMMaVTkBMp2c_w@mail.gmail.com>
Hi Michal,

> The receiver in your example is $x, an untyped local, so $x->length()
> doesn't dispatch at all.

Fine, then take the corrected version. Every narrowing you apply to keep
dispatch "locally provable" makes the feature worse, because the property
that matters to a user is the one being trimmed away: if `<expr>` is a
string, `<expr>->trim()` should work. Not "works if you assigned it to a
typed local first," not "works unless it came from a bare function call,"
not "works depending on what's above or below it in the file."

Seeing `$s->trim()` where `$s` is a string, I should know it works. A
construct where identical AST dispatches or doesn't depending on file
position, a redundant cast, or whether a declaration sits above or below
the call is not something people can reason about.

> restrict that form to internal functions, where the compiler and the
> analyser agree unconditionally

This is more fragile than the problem it fixes. A static analyser often
can't tell whether a function is "internal." A function added as a builtin
in 9.1 but provided by a polyfill on 9.0: a project supporting both has no
single answer. The same call is internal on one target and userland on the
other, so the analyser can't soundly decide whether it dispatches. That's a
language construct whose validity depends on the runtime present.

Also, this does not take into consideration unresolved names at compile
time, calling `strlen($s)->multiply($y)` inside namespace `App` with no
imports. How would this be handled? Would it also not work?

> The definitions aren't hidden. They're ordinary .stub.php files in core
> (str.stub.php, ...), which is what PhpStorm, PHPStan and Psalm already
> consume.

This isn't accurate, and I maintain Mago, so I can speak to it directly. We
don't consume PHP's `.stub.php` files. Neither do PHPStan or Psalm; they
ship their own stub sets, because core's stubs aren't expressive enough for
analysis: no conditional return types, no generics, no ranges. Publishing
`str.stub.php` doesn't give analysers what they need.

PHPStan: https://github.com/phpstan/php-8-stubs/tree/main/stubs
Psalm: https://github.com/vimeo/psalm/tree/6.x/stubs
Mago:
https://github.com/carthage-software/mago/tree/main/crates/prelude/assets/extensions

> an analyser has to special-case scalar dispatch regardless, since a
> string isn't an object

It doesn't. If `$s->trim()` works whenever `$s` is a string, the analysis
is clean: I resolve string methods against whatever interface defines them,
as if `$s` were an instance of it. What forces a hack is your design, a
construct valid or invalid for identical AST depending on file order and
surrounding declarations. I don't know how I'd model that soundly. Your
approach creates the special-casing you say is unavoidable.

> That last one is taste

The "should scalars have methods" part is taste. But this already existed,
unrestricted, and worked: Nikita's `scalar_objects` (
https://github.com/nikic/scalar_objects) dispatches on the runtime type, so
`$str->length()` works for any string, regardless of where it came from.

>  so I won't try to talk you out of a no.

You don't have to :P I don't vote.

Cheers,
Seifeddine
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.