RFC: Anonymous Class Lexical Scope

php.internals

Joe Watkins

10 years ago
Morning Internals, Please review the following RFC: https://wiki.php.net/rfc/lexical-anon A look at the patch from those of you that do that would be good :) Cheers Joe

Nikita Popov

10 years ago
On Tue, Apr 19, 2016 at 3:31 PM, Joe Watkins <pthreads@pthreads.org> wrote:
> Morning Internals, > > Please review the following RFC: > > https://wiki.php.net/rfc/lexical-anon > > A look at the patch from those of you that do that would be good :) >
Hey Joe, The syntax and semantics proposed in this RFC don't sit quite well with me. Especially the fact that a use($foo) on the class is then used as $this->foo in methods is non-intuitive to me, as it differs from how the same syntax behaves on closures. I'd like to suggest an alternative syntax: $foo = 42; return new class { private $bar = $foo; public function getBar() { return $this->bar; } } That is, allow properties inside the anonymous class to be initialized based on values from the surrounding scope. This is more explicit (clearly shows that a property is being created), it allows explicit control over the visibility and, depending on implementation, might be more flexible with regards to the values it accepts. It probably doesn't make sense to restrict this to specific expressions, so all of return new class { private $a = $var; private $b = $obj->prop; private $d = $obj->prop ?? 'default'; // ... } could be fine. Thanks, Nikita

Ivan Enderlin @ Hoa

10 years ago
On 19/04/16 16:18, Nikita Popov wrote:
> On Tue, Apr 19, 2016 at 3:31 PM, Joe Watkins <pthreads@pthreads.org> wrote: > >> Morning Internals, >> >> Please review the following RFC: >> >> https://wiki.php.net/rfc/lexical-anon >> >> A look at the patch from those of you that do that would be good :) >> > Hey Joe, > > The syntax and semantics proposed in this RFC don't sit quite well with me. > Especially the fact that a use($foo) on the class is then used as > $this->foo in methods is non-intuitive to me, as it differs from how the > same syntax behaves on closures. I'd like to suggest an alternative syntax: > > $foo = 42; > return new class { > private $bar = $foo; > public function getBar() { return $this->bar; } > } > > That is, allow properties inside the anonymous class to be initialized > based on values from the surrounding scope. This is more explicit (clearly > shows that a property is being created), it allows explicit control over > the visibility and, depending on implementation, might be more flexible > with regards to the values it accepts. It probably doesn't make sense to > restrict this to specific expressions, so all of > > return new class { > private $a = $var; > private $b = $obj->prop; > private $d = $obj->prop ?? 'default'; > // ... > } > > could be fine.
I could not express better than you, so big +1 for your proposal so far.

Dmitry Stogov

10 years ago
On 04/19/2016 05:18 PM, Nikita Popov wrote:
> On Tue, Apr 19, 2016 at 3:31 PM, Joe Watkins <pthreads@pthreads.org> wrote: > >> Morning Internals, >> >> Please review the following RFC: >> >> https://wiki.php.net/rfc/lexical-anon >> >> A look at the patch from those of you that do that would be good :) >> > Hey Joe, > > The syntax and semantics proposed in this RFC don't sit quite well with me. > Especially the fact that a use($foo) on the class is then used as > $this->foo in methods is non-intuitive to me, as it differs from how the > same syntax behaves on closures. I'd like to suggest an alternative syntax: > > $foo = 42; > return new class { > private $bar = $foo; > public function getBar() { return $this->bar; } > }
This syntax is definitely better, less magic and more powerful.
> > That is, allow properties inside the anonymous class to be initialized > based on values from the surrounding scope. This is more explicit (clearly > shows that a property is being created), it allows explicit control over > the visibility and, depending on implementation, might be more flexible > with regards to the values it accepts. It probably doesn't make sense to > restrict this to specific expressions, so all of > > return new class { > private $a = $var; > private $b = $obj->prop; > private $d = $obj->prop ?? 'default'; > // ... > } > > could be fine.
Is this for anonymous classes only? Only for property default values or something else? Also, instead of lexical scoping, we may use generic approach. return new class<A=&$var,B=$obj->prop,C=$obj->prop ?? 'default'> { private $a = A; private $b = B; private $d = C; }; But good design of generics would take a lot of ... Thanks. Dmitry.

Rowan Collins

10 years ago
Nikita Popov wrote on 19/04/2016 15:18:
> I'd like to suggest an alternative syntax: > > $foo = 42; > return new class { > private $bar = $foo; > public function getBar() { return $this->bar; } > } > > That is, allow properties inside the anonymous class to be initialized > based on values from the surrounding scope.
The problem with this is that it violates PHP's normal scoping rules, because the variable is crossing scopes "uninvited". It makes it more explicit where the variable is going *to*, but hides where it is coming *from*. Perhaps a Trait-like syntax might work? $foo = 42; return new class { use $foo as private $bar; public function getBar() { return $this->bar; } } This makes it clear both that $foo is being imported from somewhere, and that it is being saved to a particular private member. Regards,
-- Rowan Collins [IMSoP]

Rowan Collins

10 years ago
Hi Nikita, I was thinking some more about your suggestion: Nikita Popov wrote on 19/04/2016 15:18:
> $foo = 42; > return new class { > private $bar = $foo; > public function getBar() { return $this->bar; } > }
If we allowed this, would it make sense to also allow it in named classes? This is currently illegal [https://3v4l.org/Kp0ep]: $foo = 42; class X { private $bar = $foo; public function getBar() { return $this->bar; } }
> It probably doesn't make sense to > restrict this to specific expressions, so all of > > return new class { > private $a = $var; > private $b = $obj->prop; > private $d = $obj->prop ?? 'default'; > // ... > } > > could be fine.
We don't allow importing expressions into closures, nor are any of those property initialisations valid in the definitions of named classes, so I think allowing them here would be odd. Regards,
-- Rowan Collins [IMSoP]

Jesse Schalken

10 years ago
I'm not sure it matters, but there is some precedent for this in JavaScript/ES6: function foo(bar) { return new class { myBar = bar; getBar() { return this.myBar; } }(); } console.log(foo('hello').getBar()); // "hello" (You can actually reference outer symbols anywhere in the definition of a JS class, so I could have used "bar" directly in "getBar" without going through a property.) On Wed, Apr 20, 2016 at 12:18 AM, Nikita Popov <nikita.ppv@gmail.com> wrote:

Joe Watkins

10 years ago
Can we just halt this conversation for a few days. That implementation is totally and utterly wrong, it's so obvious this morning ... I'm almost embarrassed. I must say that of all the suggestions made so far for syntax, I still prefer use(). While the idea to allow symbols and expressions is very cool, it will create so much confusion that an anonymous class can do things like set objects as member properties but other classes can't. The syntax also looks absolutely terrible when it comes to refs: class { public $thing = &$this->thing; } This is almost literally *backwards*. All of the solutions suggested so far also create rather a lot of repetition for anything that isn't example code. Anyway, the implementation is broken, let's come back to this discussion later on, please. Cheers Joe On Wed, Apr 20, 2016 at 4:54 AM, Jesse Schalken <me@jesseschalken.com> wrote:

Joe Watkins

10 years ago
Oh, I fixed it ... I think ... carry on ... Cheers Joe On Wed, Apr 20, 2016 at 7:01 AM, Joe Watkins <pthreads@pthreads.org> wrote:

Nicolas Grekas

9 years ago
Hi everyone, having worked a bit with anonymous classes these days, the current boilerplate is really high to inject some local context into them. So big +1 from me for the target. On the proposed syntax, I really prefer Nikita's proposal: return new class {
> private $a = $var; > private $b = $obj->prop; > private $d = $obj->prop ?? 'default'; > // ... > } >
The "functional equivalent" wording in the RFC has a big drawback: it looks like the parent constructor is overridden, which would be a really nasty side effect. It may not be the case in the implementation, but the the RFC needs to be clarified IMHO. If we were to need more closure-like syntax, we may even use "use" like this: return new class use ($var, $obj) { private $a = $var; private $b = $obj->prop; private $d = $obj->prop ?? 'default'; // ... } I really hope this new syntax (which ever raises the consensus) can be ready soon :) Nicolas