Re: [RFC] [Discussion] nameof
| From: | Larry Garfield | Date: | Thu, 25 May 2023 17:29:14 +0000 |
| Subject: | Re: [RFC] [Discussion] nameof | ||
| References: | 1 2 3 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-120399@lists.php.net to get a copy of this message | ||
On Thu, May 25, 2023, at 3:21 PM, Robert Landers wrote:
> On Tue, May 16, 2023 at 11:56 PM Larry Garfield <larry@garfieldtech.com> wrote:
>>
>> On Sat, May 13, 2023, at 7:27 AM, Robert Landers wrote:
>> > Hello Internals,
>> >
>> > It is with much trepidation and excitement that I'd like to announce
>> > the
nameof RFC (https://wiki.php.net/rfc/nameof). It
>> > has changed
>> > quite a bit in the last couple of days, so if you haven't seen the
>> > latest draft, please check it out.
>> >
>> > Essentially, it allows using nameof() anywhere a
>> > string can be used,
>> > even in static contexts. From a developer's perspective, it is a
>> > string and from the engine's perspective, it is also mostly a string
>> > (depending on how deep we want to go on error checking -- see the
>> > RFC).
>> >
>> > If anything is unclear or if I missed something, please let me know.
>> >
>> > Robert Landers
>> > Software Engineer
>> > Utrecht NL
>>
>> Some concrete use cases that I know I run into, and would thus be what I'd hope an RFC
>> like this would resolve:
>>
>> Router::addRoute('\my\space\my_action_function`);
>>
>> Right now that has to be a string with a full namespace. You cannot use a FCC here,
>> because you want to use this data to compile the router somehow. So in this case we want the full
>> namespace. (Ignore function autoloading for now.)
>>
>> Router::addRoute(MyClass::actionMethod);
>>
>> Various frameworks have different custom syntaxes for this case. It's also not clear
>> if this refers to a static method, or a "instantiate this out of the container first and then
>> call this method" approach. In these cases, we would want the full class name, and the method
>> name on its own, as separate strings.
>>
>> In my FP library, I have code like this:
>>
>> function prop(string $prop): \Closure
>> {
>> return static fn (object $o): mixed => $o->$prop;
>> }
>>
>> function method(string $method, ...$args): \Closure
>> {
>> return static fn (object $o): mixed => $o->$method(...$args);
>> }
>>
>> Which you can then use in a pipe, like so:
>>
>> pipe($someObject, method('foo'), prop('bar'));
>>
>> Or, more realistically, you'd use method() and prop() in a map or filter call within
>> the pipe.
>>
>> In this case, you want just the method/property name on its own, without a namespace,
>> because it will be used in the context of an object to be named later.
>>
>> How would nameof() handle each of these cases?
>>
>> --Larry Garfield
>>
>> --
>> PHP Internals - PHP Runtime Development Mailing List
>> To unsubscribe, visit: https://www.php.net/unsub.php
>>
>
> Hey Larry,
>
> I did not ignore you. I somehow missed your question while researching
> something after reading the first half of your email last week -- I,
> apparently, never made it to the second half until today. I'll get to
> that in a sec.
Not to worry.
>> pipe($someObject, method('foo'), prop('bar'));
>>
>> Or, more realistically, you'd use method() and prop() in a map or filter call within
>> the pipe.
>>
>> In this case, you want just the method/property name on its own, without a namespace,
>> because it will be used in the context of an object to be named later.
>
> So, for example, you could replace your pipe with:
>
> pipe($someObject, method(nameof($someObject->foo(...))),
> prop(nameof($someObject->bar)))
>
> It looks a bit wordy, but PHP differentiates between properties and
> methods by using a parenthesis/bracket (hence if you want to call a
> property that is actually a closure, you have to write ($this->prop)()
> otherwise it will look for a method called prop and fail to find it).
>
> At runtime, you would receive a warning on this line if $someObject is
> missing a method called foo or a property called bar (if the
> error-detecting version is chosen). This would result likely result in
> an actual error in your method() and prop() functions. However, it
> should make debugging from logs a bit easier since you'll (hopefully)
> notice the warning.
I figured it would be something like that. I don't know how it would be done better, but it
also makes the code notably more verbose, and clumsy with the extra ()s. So, I'm not sure if
it's a net-win or not.
> Now, why I didn't answer you and only made it half way through your email...
>
> Much of the earlier discussion was about functions/consts being
> fully-qualified names. I was mostly against it, because I was offering
> 'both' qualified and unqualified. However, your initial examples
> really hit home for me and I did a bit of spelunking in various
> popular PHP codebases for class::name (since that is really the only
> similar thing) and for stringified names of things. I'm now convinced
> that FQL's should be returned for functions, and constants (but not
> methods and properties).
Mission accomplished, then. :-) And yes, I agree with that conclusion.
> Thus, your example:
>
> Router::addRoute('\my\space\my_action_function`);
> Router::addRoute(MyClass::actionMethod);
>
> could be rewritten as
>
> use function \my\space\my_action_function;
> Router::addRoute(nameof(my_action_function));
> Router::addRoute(nameof(MyClass::actionMethod(...)));
>
> I'll have to think about how to word this in RFC, but I'll do so as
> soon as I figure it out.
>> use function \my\space\my_action_function;
>> Router::addRoute(nameof(my_action_function));
>> Router::addRoute(MyClass::name . '::' . nameof(MyClass::actionMethod(....))); //
>> or something since the class name is not returned with nameof().
The function one seems reasonable. The method version, though, looks super ugly and hard to
implement. I'd be much more likely to just split it into two arguments and leave the second as
a string, TBH.
Using a :: pseudo-constant suffix rather than a ()-based construct would help with the syntax
clumsiness in both cases, but wouldn't entirely eliminate it. I don't have a really great
answer here, unfortunately. I think I'd marginally prefer a ::name suffix at this point just
for syntactic convenience, but I'm still undecided if the overall tradeoff is worth it. (For
::name, probably, as that would be a much lighter syntax impact for those unusual cases that
it's needed.)
--Larry Garfield