Hello Christian,

Sunday, January 18, 2009, 11:58:29 PM, you wrote:

> Hi Marcus,

>>> "Convoluted"? "Mess"? Are you kidding me? It's standard usage of access 
>>> handlers.
>> 
>> It is a mess right now. You assign a closure to another method and get
>> access to the original owners private members. That is not only unexpected
>> and contradicting anything that any oyther language ever but but also
>> violates our very own basic ideas. That is what I call a mess.

> You could also call Javascript's behaviour confusing. A closure is per

And I could say that what the two of you designed ofr PHP is not a design
but a very confusing incoherent implementation that is based on the lack
of being able to get support for something else in the underlying c
implementation. You took somethign very special and made it behave somewhat
different making it even more specific and different from anyone's
expectations, excluding people that understand the underlying c level
issues.

Either way, we should imo, simply disable $this for clusresm get and get
rid of any dynamic class binding mess or make them work. Doing a lot of
unexpected behavior is in my opinion very very bad.

> definition a function that encloses its scope at creation time. E.g. if
> you have the following (in Javascript, PHP, doesn't matter):

> var foo;
> var closure = function () {
>         alert (foo);
> };

> The current scope variable foo is inherited. The question is: Why
> shouldn't the same also happen for the variable this? In the closures
> implementatios Dmitry and I designed for PHP, it does. Admittedly, $this
> is a special variable because it's implicitly available in normal
> methods and thus we decided that for closures you don't need to do "use
> ($this)" either.

> So the question is: Why does Javascript change the pointer to the this
> variable upon calling a method? The answer is simple: Because there is
> NO OTHER WAY to define object methods in Javascript. You *always* have
> to use object.method = function () { }; or Something.prototype.method =
> ... in order to define a callable method. There is no other way. Because
> of that, Javascript defines the behaviour with $this.

> PHP, on the other hand, since it already does have a method for creating
> normal class methods (simply define them in the class { } block), does
> not need such a mechanism for normal OOP.

> Also, implementing this in PHP may give quite some headaches. Take for
> example the following code:

> interface Some_Filter {
>   public function accept ($value);
> }

> class Closure_Filter implements Some_Filter {
>   private $closure;
>   public function __construct (Closure $closure) {
>     $this->closure = $closure;
>   }
>   public function accept ($value) {
>     // Or something similar, see below
>     return call_user_func ($this->closure, $value);
>   }
> }

> class Foo {
>   private $min, $max;

>   public function bar () {
>     $filter = new Closure_Filter (function ($value) {
>       return $value >= $this->min && $value <= $this->max;
>     });
>     $data = $something->doSomethingElse ($filter, $data);
>   }
> }

> Now, basically, the idea behind this code should be clear: We want to
> define a filter, there's an interface for that filter that any class may
> define and there's a simple wrapper class for Closures for filters that
> are supposed to be extremely simple. I don't think this example is
> convoluted, one could easily imagine a similar design in the real word
> (probably a bit more complex, but nevertheless).

> The filter closure is now defined in the class Foo. Thus one would
> assume that the closure is bound to the $this of the Foo object (once
> created). But since that closure is passed to the constructor of the
> Closure_Filter class, the $this would be rebound according to your
> proposal. Thus, when invoking the method, the closure would now try to
> access the ->min and ->max properties of Closure_Filter class - which is
> clearly not the intention.

> Of course, there are possibilites to circumvent that: 1) Copy the min
> and max properties to local scope and bind them with use() to the
> closure. Or 2) Don't store the closure directly inside a property of the
> Closure_Filter class but in an array so $this doesn't get rebound. But
> clearly, in my eyes, that is some kind of hackish workaround that really
> sucks.

> Also, with the implementation Dmitry and I wrote, it is very clear what
> the semantics are $this: It is always bound at creation time and that's
> it. Just to make a comparison:

> // Variant 1:
> return call_user_func ($this->closure, $value);
> // Variant 2:
> $closure = $this->closure;
> return $closure ($value);
> // Variant 3:
> return $this->closure->__invoke ($value);
> // Variant 4:
> return $this->closure ($value);

> Now, the original implementation:
>   Variant 1: $this bound to Foo class
>   Variant 2: $this bound to Foo class
>   Variant 3: $this bound to Foo class
>   Variant 4: doesn't work, because methods and properties have a
>              different namespace

> Now, an implementation where *all* four variants bind to the
> Closure_Filter class:
>   Variant 1: $this bound to Closure_Filter class
>       -> Inconsistent: call_user_func ($this->normal_method) will first
>                        cause "undefined property" and then "invalid
>                        callback" errors
>   Variant 2: $this bound to Closure_Filter class
>       -> Hmm, so this basically allows for the following code:
>                $closure = function ...;
>                $object->closure = $closure; // MAGIC happens!
>                $closure = $object->closure; // $closure changed - WTF?!
>   Variant 3: $this bound to Closure_Filter class
>       -> Ok, this really doesn't matter either way, using __invoke
>          directly looks a bit weird anyway.
>   Variant 4: $this bound to Closure_Filter class
>       -> Calling properties directly will certainly cause resolution
>          order headaches. Since that was the MAIN point on the list that
>          was discussed before my posting you will have to admit it at
>          least is not obvious what the resolution order should be.

> Ok, you could say *ONLY* variant 4 should be added with dynamic and
> temporary (i.e. call-time and for the duration of the call) rebinding of
> the $this context. But that would also cause confusion: Why should $this
> for the *same* property on some occasions a first object and on some
> another? It doesn't make any sense!

> Now, I would call this certainly a bigger mess than the inconsistency
> wrt. Javascript. Since PHP is already different from Javascript (access
> modifiers, "normal" object methods, different namespaces for methods and
> properties), I don't really see the point in forcing something which
> will create problems with the concepts PHP already has.

> ----------------------- snip ------------------

> However, having said that, I may have a solution which will make
> everybody (more or less at least) happy:

> Since the main problem of re-binding closures to different objects is
> the obscure magic just by assignment, why not make that magic "public"?

> I.e.: Add a method bindTo() to the Closure class which returns an
> identical copy of the closure (same bound variables etc.) with the
> EXCEPTION that the new variables are already bound. This allows for
> prototyping in the way that you want it BUT ensures that no silent magic
> occurs that creates problems.

> Example:
> $func = function ($a, $b) {
>   return $a + $b + $this->bias;
> };
> $object->add = $func->bindTo ($object);

> Additionally, a static method bind() could be added for direct assignments:

> $object->add = Closure::bind ($object, function ($a, $b) {
>   return $a + $b + $this->bias;
> });

> The bindTo/bind functions could also check the current class scope and
> ensure that the class scope of the newly bound closure has the same
> access level to the object as the current class - that would enforce
> access modifiers. Whether or not we want that is probably a matter of
> debate.

> Another idea which *could* be discussed (I'm not sure about it myself)
> is that if you add variant 4 from above (i.e. calling object properties
> like methods directly) - which you probably want to do if you want to
> allow $this rebinding - and we have finally figured out some sane
> resolution rules, one could also add a E_WARNING message when the $this
> pointer of the closure does not match the $this pointer of the object
> for which the closure is called as a method. So, for example:

> $closure = function () { return $this->value; };
> $object->get1 = $closure;
> $object->get2 = $closure->bindTo ($object);

> call_user_func ($object->get1);
> call_user_func ($object->get2);
> $f = $object->get1; $f ();
> $f = $object->get2; $f ();
> $object->get1->__invoke ();
> $object->get2->__invoke ();
>   // -> No warnings
>   //       BECAUSE (!) this syntax makes it clear to the user that
>   //       he is calling *some* closure, but not necessarily a closure
>   //       bound to the specific object
> $object->get1 ();
>   // -> Warning: Closure called directly but object scope does
>   //    not match object closure is being called for!
>   // -> HOWEVER, execute the closure and DON'T rebind it
> $object->get2 ();
>   // -> No warning (match)

> The check should of course be intelligent enough to detect the
> following correctly:

> $this->foo = function () { ... };
> $this->foo ();
>   // -> No warning (match, since assigned from inside the own object)

> ------------------ snip again --------------------

> So, to sum up my posting:

> 1. Stance on direct method calling $foo->closure_property ()

>        - As long as you figure out some sane and consistent resolution
>          rules: Fine with me.

> 2. Stance on *automatic* re-binding of $this

>        - Really big convoluted mess
>        - PHP already does things differently from Javascript, has
>          different concepts.
>            => I'm against it.

> 3. Alternative proposal for making re-binding possible but making
>    sure this is done explicitly:

>        - Figure out some sane resolution rules
>        - Closure::bind and Closure->bindTo

> 4. Additional proposal to 3:

>        - Enforce class scope and thus access modifiers when using
>          Closure::bind / Closure->bindTo

> 5. Additional propsoal to 3:

>        - Warning when using direct-calling and the closure is not
>          bound to the object for which the closure is called.

> Regards,
> Christian




Best regards,
 Marcus


-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to