Re: [PHP-DEV] [RFC] prototyping
Hi Marcus, I've just got an idea how prototyping may be implemented without magic you solution had. We can use a special function function attach_method($object, $name, $closure); This function should clone the $closure bind $this of clone of $closure to $object and assign it into $object-name (the original $closure should be unchanged). However it won't make possible to call it as $object-name() anyway. Also, it's a question what should attach_method() do if $closure already has $this (rebind it or throw an exception). Thanks. Dmitry. -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [RFC] Object extension, $this binding of closures, chat results (was: Re: [PHP-DEV] [RFC] prototyping)
On Thursday 22 January 2009 8:21:28 am Christian Seiler wrote: Hi everybody, We had that chat that Lukas announced yesterday and I promised Lukas to sum up the results of that chat. Problem was: There were only four people really there: Stas, Lukas, David and me. Lukas was interested in getting results in order to be able to release PHP 5.3 beta 1, Stas and I basically agreed on everything and unfortunately David didn't stay long enough in order to actually have a real discussion. I'm not assigning blame here for not coming to anyone, I just wanted to make clear that we actually couldn't reach a consensus - or well, rather, Stas and I could but I don't think that's the way it should be. However, Lukas wants to release 5.3 beta 1 and I can really understand him that he doesn't want to drag this out infinitely. I've written up the results of the discussion I had with Stas in the Wiki for everyone to read. I hope there's enough structure in it to make the main points clear. Please read it thoroughly before replying on-list in order to actually have a constructive dialogue. Here it is: http://wiki.php.net/rfc/closures/object-extension IMO, a very large part of the problem is the implicit binding of $this when a closure happens to be defined inside a method of a class. Every other variable that the closure inherits needs to be defined explicitly; that not only helps the parser but helps human readers. Having $this be magically bound seems like a cause for confusion later on down the road. That said, I very much like the proposal given in the RFC. Rebinding of a closure should be an explicit operation that a future reader of the code can visually see and therefore know about. That removes any potential WTFs when $this is suddenly $that even though you didn't mean for it to be. Regarding the details, wherein lives the devil: - If a closure is bound to an object and does NOT make use of $this, do we still want to throw a warning if it is called? In that case the binding is irrelevant anyway. (That goes back to the point about an implicit initial bind, which I am still not convinced is wise.) - IMO, once a closure has been bound to an object, its $this should have all the same access as a method defined literally on that object's class. That is, it should have private/protected access the same as a normal method. - I am a little unclear on what the expected cloning behavior is. Do closures have a clone method? Take the following example: class A { function getClosure() { $var = new Foo(); return function() use ($var) { return $var-something; } } } $a = new A; $o = new stdClass(); $closure = $a-getClosure(); $o-foo = $closure-bindTo($o); Now, are $closure-var and $o-foo-var the same object or no? I'm assuming they are from the RFC, but then how do you implement a deep clone of the closure on binding if you need to? -- Larry Garfield la...@garfieldtech.com -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [RFC] prototyping
Am 20.01.2009 um 18:41 schrieb Christian Seiler: Hi, maybe an IRC meeting is the easiest way to come to an agreement. How about tomorrow evening 21:00 CEST in #php.closures on freenode? Just for clarification: I assume you mean Wednesday, January 21st, 19:00 UTC (CEST == UTC+2) and thus 20:00 CET? Would be fine with me. I'm pretty confident he didn't mean summer time, so it's likely 21:00 CET or 20:00 UTC. smime.p7s Description: S/MIME cryptographic signature
Re: [PHP-DEV] [RFC] prototyping
On 21.01.2009, at 10:04, David Zülke wrote: Am 20.01.2009 um 18:41 schrieb Christian Seiler: Hi, maybe an IRC meeting is the easiest way to come to an agreement. How about tomorrow evening 21:00 CEST in #php.closures on freenode? Just for clarification: I assume you mean Wednesday, January 21st, 19:00 UTC (CEST == UTC+2) and thus 20:00 CET? Would be fine with me. I'm pretty confident he didn't mean summer time, so it's likely 21:00 CET or 20:00 UTC. actually i did not think and it was just a random suggestion :) since Christian wasn't forceful enough to settle things on a time, lets make it 21:00 CET or 20:00 UTC, tonight. the channel is already open and people are ideling in there. however i would suggest that most of the talking should be done by the key people involved (marcus, stas, dmitry, christian, stan). other people are of course also invited to participate, but in order to let the discussion on irc to not go totally crazy it might be better that people pick their favorite of the 5 and just PM them with their thoughts, so that they can then relay them. for this purpose people will need a registered account on freenode IIRC. but there are no official rules .. so when in doubt common sense/courtesy rules :) regards, Lukas PS: i will probably not participate .. though i will be idleing in the channel. -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [RFC] prototyping
Hi, maybe an IRC meeting is the easiest way to come to an agreement. How about tomorrow evening 21:00 CEST in #php.closures on freenode? regards. Lukas -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [RFC] prototyping
Hi, maybe an IRC meeting is the easiest way to come to an agreement. How about tomorrow evening 21:00 CEST in #php.closures on freenode? Just for clarification: I assume you mean Wednesday, January 21st, 19:00 UTC (CEST == UTC+2) and thus 20:00 CET? Would be fine with me. Regards, Christian -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [RFC] prototyping
Hi! maybe an IRC meeting is the easiest way to come to an agreement. How about tomorrow evening 21:00 CEST in #php.closures on freenode? ok -- Stanislav Malyshev, Zend Software Architect s...@zend.com http://www.zend.com/ (408)253-8829 MSN: s...@zend.com -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [RFC] prototyping
Hi! 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 Could you give a code example? I'm not sure I understand what you mean by access (or assign closure to a method for that matter) - if access provided by the closure, it's exactly the intent of the closure. If some other access, then please explain which one. -- Stanislav Malyshev, Zend Software Architect s...@zend.com http://www.zend.com/ (408)253-8829 MSN: s...@zend.com -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [RFC] prototyping
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
Re: [PHP-DEV] [RFC] prototyping
Hello Stanislav, Monday, January 19, 2009, 9:32:09 AM, you wrote: Hi! 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 Could you give a code example? I'm not sure I understand what you mean by access (or assign closure to a method for that matter) - if access provided by the closure, it's exactly the intent of the closure. If some other access, then please explain which one. class Foo { private $myVeryOwnData; function bla { $this-bigMEss = function () { return $this-myVeryOwnData; } } } $o = new Foo; $f = $o-bla; echo $f(); class Bar { function baz{} { $this-xyz = $o-bla; // or $f; // I am very sorry but I would expect the closure to rebind $this as // $this always points to the bound object. And how is this different? // If the different is that the closure is already bound and that is // what you want, then we need ways to do just that. And not inventing // new ways to deal with $this. } } $onother = new stdClass; $another-blabla = $f; // yet again a very unexpected $this. If this really was to be bound to a $this at the creation, then we'd put it into the scope binding list and not have it be handled implicitly. But then we cannot do that with the current c level api. So instead you created brand new rules, which unfortunately is the PHP way. So how would I copy a closure instead of rebinding it? I would use PHP's current established semantics for that. That is I'd do: $this-closure = clone $closure; $this-closure = $closure-copy(); How would I prevent automatic $this binding? $closure = static function() -- Stanislav Malyshev, Zend Software Architect s...@zend.com http://www.zend.com/ (408)253-8829 MSN: s...@zend.com Best regards, Marcus -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [RFC] prototyping
Hi Marcus, 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. Huh? The current implementation is by design, not because of any limitations on C level. If you are refering to the following sentence in your answer to Stanislav: If this really was to be bound to a $this at the creation, then we'd put it into the scope binding list and not have it be handled implicitly. But then we cannot do that with the current c level api. The fact is: We could very easily. We decided against it (it's somewhere in the internals archives from over half a year ago, I'm too lazy to look for it). So, if I get you, you say: If $this is only bound at closure creation time, then you should add $this to use(), i.e.: $f = function () use ($this) { ... }; Well, if you rather want that syntax instead of static/non-static, you could simple edit zend_compile.c and do the following: - change zend_do_begin_lambda_function_declaration: set fn_flags to ZEND_ACC_STATIC by default (and remove the is_static parameter from that function) - change zend_do_fetch_lexical_variable: In the case $this was specified as a lexical variable, remove the ZEND_ACC_STATIC flag from the function flags and return immediately (and remove the current errormessage) - remove the T_STATIC from the grammar definition in zend_lang*.y Unified diff of that should be max. 100 lines long. So, this is NOT a limitation on the C level, this is a deliberate design decision. And, btw., it is not a design decision I care about very much - if there's a majority to change it, I've no problem with that. 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. No, we did not do any such thing. People who do a lot of Javascript expect the behaviour you describe (i.e. re-binding), but that does not necessarily mean everybody does. And if you read postings made by beginners with Javascript in beginners' web forums or beginners' mailing lists, the re-binding of the this variable is a VERY confusing thing for beginners and not natural at all. You may have grown accustomed to this behaviour, but please do not make the mistake of thinking that your expectations are the expectations of the rest of the world. I do acknowledge that some people like you will expect this behaviour due to their background in Javscript. I also posted in HUGE DETAIL why I find your approach to be *very* problematic. I also went into detail on why you find your approach more natural and why Javascript is different than PHP in this aspect so you can't simply copy the concept from one to another. I also added a proposal which contains an alternative that may not be ideal but may satisfy all the needs. But instead of really replying to that, you simply took the first sentence, used my admittedly not very ideal wording as polemic and repeated your statement from before. I don't think this will get us anywhere. So, I would be very happy if you could 1) acknowledge that what you would expect is not necessarily that what everybody else would expect (some others certainly, but not everybody), 2) realize that many (albeit not all) things in the current implementation are NOT there because of C limitations but are deliberate (see above for an example) and 3) have another look at my previous posting, think about it for some time and THEN reply to it. Because the intention behind my posting was NOT to say you're wrong, you ... but rather to say I get where you are coming from but have a look at this example, your proposal will cause additional problems. And your expectations do not necessarily match mine. But I don't want to dismiss your idea completely, so here's an alternative idea that came to my mind, please think about it. Regards, Christian PS: By the way, Python does not rebind closures that enclose the current object handle either: class Foo: def __init__ (self, v): self.v = v def getClosure (self): def closure (w): return self.v + w return closure o1 = Foo (2) o2 = Foo (3) # both are o1.getClosure not o2! o1.xyz = o1.getClosure() o2.xyz = o1.getClosure() print o1.xyz (2) # 4 print o2.xyz (2) # 4 (Yes, that's due to the different concept of the 'self' variable in Python compared to the way PHP and Javascript use $this, but it just demonstrates that one has to look at the other concepts that are implemented in the language in order to get something consistent. That's precisely my argument for PHP, too.) -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit:
Re: [PHP-DEV] [RFC] prototyping
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 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
Re: [PHP-DEV] [RFC] prototyping
Hello Stanislav, Wednesday, January 14, 2009, 5:29:02 PM, you wrote: Hi! That is one example of convoluted code that is already possible. If a developer creates such a mess is his fault. 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. Best regards, Marcus -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [RFC] prototyping
Hi, Ok, my proposal to drop OO support from Closures was exactly embraced. All I want you guys to think about is how you want to deal with this in terms of the PHP 5.3 release. If no decision is found and implemented over the weekend, this topic will essentially cement the need for an additional beta .. or at worse staying at alpha. I am playing a frisbee tournament this weekend, so I hope by Sunday evening there is a decision on how to proceed in regards to Closures/ Prototypes. Depending on what that decision is, we will figure out some schedule. The current plan would see a freeze on Monday, giving us Tuesday to do build fixing, Wednesday to package and Thursday to release. regards, Lukas -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [RFC] prototyping
Hi I have tried to catch up to the discussion yesterday evening. I must admit its not very trivial for me to understand the entire scope of the issues. I am especially worried about things becoming too magical (although in general I am pro giving end users all the power in the world). At this point I am beginning to wonder if we maybe should drop any special OO handling from closures for now and leave it to the next bigger release to decide if to readd it. We have worked through the challenge of getting namespaces worked out in 5.3, adding yet another increasingly heavy feature to the syntax would delay us if we really want to make sure we get it right. Screwing up would get us into a world of hurt and to me the main use case we wanted to address was making it easier to define callbacks for our various internal functions. This is just my managament level view point given the discussion at this stage and the goal to get PHP 5.3 out the door without delay from any feature (since we have enough features to release even if we remove one or two features). regards, Lukas -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [RFC] prototyping
On Thu, Jan 15, 2009 at 10:56:01AM +0100, Lukas Kahwe Smith wrote: We have worked through the challenge of getting namespaces worked out in 5.3, adding yet another increasingly heavy feature to the syntax would delay us if we really want to make sure we get it right. Screwing up would get us into a world of hurt and to me the main use case we wanted to address was making it easier to define callbacks for our various internal functions. I was wondering, why don't you simply mark the feature as experimental, so people that use it know that it might change unexpectedly from one version to another, breaking their scripts. Something similar to the Linux Kernel features approach. I've never seen this approach used in PHP development process, I've always been seeing the feature freeze - support for a long time - deprecate - remove in the next major version approach. Marking a particular language feature as experimental would allow you to drop it or to change it without notice even in a patchlevel release (5.3.1), resulting in a way quicker and more dynamic development process. The benefits? Nobody (but me...) would install an unstable version of PHP in a production environment, but probably some people would taste an experimental feature included in a stable version. Just my 2 cents. Regards, -- Giovanni Giacobbi -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [RFC] prototyping
Am 13.01.2009 um 22:58 schrieb Marcus Boerger: 5) A closure assigned to a property can be called directly rather than just by a temporary variable: $obj-property = function() {} $obj-property(); // valid call This will get us into trouble IMHO, as it would not behave too well with __get/__set and __call. I.e. if we have $foo-nosuchproperty() - should we call __get or __call? So far variables implemented through __get behave exactly like ones implemented through definition - but I don't understand how to reconcile it with this. Why would you call __get() here? Becasue I did that by mistake in my very first mail? You clearly have a function call and thus you only go for __call(). As of today you can already do: function __call($name, $args) { if ($this-properties[$name] instanceof 'Closure') { return call_user_func_array($this-property[$name], $args); } } Now we already have callable properties - directly callable. 1. method 2. __call() 3. property 4. __get() that would be a reasonable calling order, or: 1. method 2. property 3. __call() 4. __get() In any case, a real method (existing one or __call() overload) should have precedence over a closure in a property. Wouldn't that work? - David smime.p7s Description: S/MIME cryptographic signature
Re: [PHP-DEV] [RFC] prototyping
On 1/14/2009 01:15, Stanislav Malyshev wrote: Hi! Also, this adds very new thing to PHP - objects that change while being assigned. I am not sure it is a good thing. Well Closures are a brand new thing in PHP. So far we had nothing even remotely close to the closures we have right now. There are a lot of different features in PHP, that's not the reason to turn the engine into a salad of special cases and exceptions. That's why making Closure an object and having __invoke was a good idea - because it fits what the engine already does very well. Having special case just for one class doesn't fit it so well. you wrote. And in my opinion it also makes the most sense. A closure keeps its state. I consider $this be a part of this state. Maybe if you really need it it'd be fine to do something like $closure-setThis($object)... Why would you call __get() here? Becasue I did that by mistake in my very Imagine such code: class Contains { private $_store; function __set($n, $v) { $this-_store[$n] = $v; } function __get($n) { return $this-_store[$n]; } } Pretty standard class. Now imagine you do this: $c = new Contains(); $c-foo = bar; echo $c-foo; works well and basically you see Contains as regular object and couldn't care less it has handlers and not properties. Now this: $c-bar = function() { } $c-bar(); what happens here? You can't get the value of bar without calling __get and if you call __call instead that means you either lost $c-bar or you have two kinds of properties now - ones that call __get and ones that don't. I don't see how it is good. That is one example of convoluted code that is already possible. If a developer creates such a mess is his fault. As Marcus said, it is already possible to call properties. All we have to do is implement __call(). Not being forced to implement __call in cases where we want object augmentation with lambdas is just syntactic sugar. I agree though that a rebind()/setThis() method on the Closure object is needed, but $this should be rebound by default. Auto-magically binding $this to the current object, whatever that may be, is a missing feature that requires a developer to pass the instance as an argument to that lambda. class A { protected $_lambdas = array(); public function __set($name, $lambda) { $this-_lambdas[$name] = $lambda; } public function __call($lambda, $args) { $args = array_merge($this, $args); return call_user_func_array($this-_lambdas[$lambda], $args); } } $a = new A; $a-foo = function($this) { var_dump($this); }; $a-foo(); Now, the above code is already possible, but... by implementing callable properties a developer is spared the effort of implementing a __call() method. The only problem now is that by deciding to not implement __call() he loses a nice opportunity of passing the instance object and he's left with some ugly alternatives: class B {} $b = new B; $b-foo = function($this) { var_dump($this); }; $b-foo($b); // how would be the above call different than? $foo = function($object) { var_dump($object); } $foo($b); // Or would you believe this is just fine? $b-foo = function() use ($b) { var_dump($b); }; $b-foo(); Sooner or later, people would want that use statement to be dismissed for an auto-magically bound $this. It's also the problem of accessing protected and private members. JavaScript does automatic binding of the this instance which is actually a key point in prototyping. Well, strictly speaking this is not prototyping, is augmentation. Adding members to the JS prototype object means that *any* existing and further instances will share those members. Augmentation is just about adding members to an existing instance. Real prototyping is though possible in PHP 5.3: class A { /** * Lambdas available to all instances */ public static $lambda; public function __call($method, $args) { $method = self::$lambda; return call_user_func_array($method, $args); } } A::$lambda = function() { return 'foo'; }; $a1 = new A; $a2 = new A; echo $a1-lambda(); echo $a2-lambda(); The problem of binding $this appears again, but I believe this use case is much harder to solve because there's no $this in the moment we add lambdas to the class. Anyway, it's a point to consider. No, becasue Closure cannot be derived as it is a final class. If we change Why it's a final class? Any special reason for that? It matters how you bind static variables to it as they are taken from the context. And by the binding you keep the context. Sure all right. But we bind this in a completely different way. I see no reason to have two contexts in closure - one bound one way and another bound another way. I think context is a context - it's where the closure was created. different from an assignment outside the class? And it gets even better, if you assign that closure to another
Re: [PHP-DEV] [RFC] prototyping
Hi, On 1/14/2009 18:29, Stanislav Malyshev wrote: Hi! That is one example of convoluted code that is already possible. If a developer creates such a mess is his fault. Convoluted? Mess? Are you kidding me? It's standard usage of access handlers. I don't understand how that could be standard when the example was about calling overloaded properties, which isn't yet possible in the language, as you vigilantly observed. Now, regardless whether is convoluted or not, you must agree that is better having callable properties in the language and documenting that this won't apply for overloaded properties. In the end you don't really need callable overloaded properties as a developer already has access to the internals of a class, thus __call() is all he needs to implement them (there would be at least a magic call anyway). It's a trade off but a worthy one in my opinion. Just as right now it is being documented that in order to call a lambda assigned to an instance property you need a temporary variable, it could be easily documented that one should not expect callable overloaded properties and must instead implement __call beside the standard __get/__set. It's a WTF but a smaller one this time. Anyway, there may be stronger arguments against so I'm interested in your opinion. Thanks, I. Stan -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [RFC] prototyping
Hi! Now, regardless whether is convoluted or not, you must agree that is better having callable properties in the language and documenting that this won't apply for overloaded properties. In the end you don't really I do not see any aspect in which it would be better. need callable overloaded properties as a developer already has access to Having features in the language that do not work together is usually a bad idea. Developer using overloads would expect it to work in most cases where properties work, and if this prototyping design can not do it I think it is a sign more work is needed to solve it before we can add it. -- Stanislav Malyshev, Zend Software Architect s...@zend.com http://www.zend.com/ (408)253-8829 MSN: s...@zend.com -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [RFC] prototyping
Hello, On Tue, Jan 13, 2009 at 12:07 AM, Stanislav Malyshev s...@zend.com wrote: Hi! 1) a non static closure assigned to an instance changes the closures this to be set to the actual object: I'm not sure why would you expect this. If you have closure that has some bound $var inside, and you use it in context which has another $var, you don't expect that $var change to new scope's $var, do you? I always thought of closures as taking their variables and context with them, not changing them each time caller changes. Also, this adds very new thing to PHP - objects that change while being assigned. I am not sure it is a good thing. 4) Cloning an object assigns the properties correct. Right now we increase the refcount only instead of cloning as that is the default behavior of cloning. Since a normal variable splits on changes nothing ever notices this. For oject types that do not support cloning this is very different though. Now we cannot simply add cloning as then we'd further change closure behavior. And this way we would not fix the this pointer. Besides the issue above with changing $this I'm not sure what would proper clone do - i.e. by-val bound variables are by-val anyway, so it shouldn't matter if they are cloned, and by-ref ones should be connected anyway so again it doesn't matter. Am I missing something? BTW, why would one clone closures anyway? 5) A closure assigned to a property can be called directly rather than just by a temporary variable: $obj-property = function() {} $obj-property(); // valid call This will get us into trouble IMHO, as it would not behave too well with __get/__set and __call. I.e. if we have $foo-nosuchproperty() - should we call __get or __call? So far variables implemented through __get behave exactly like ones implemented through definition - but I don't understand how to reconcile it with this. I also don't like having special one-class check inside assignment operators just for this narrow function - it doesn't look like good generic code and I suspect for proper implementation may require instanceof check on each assignment - which would be really bad. 2) The current behavior seems inconsistent as it matters where an assignment of a closure to a proeprty is being performed. OR how a closure is being created. Of course it matters how (or, more precisely, where) the closure was created - isn't it the whole point of closure? 3) If closures get callable by property directly then we end up in a situation where we can have two methods with the same name. That means it is discussable whether we want to allow assignment of a closure to a member variable name that already exists as a private member function. This is another thing - does it mean on each assignment we'd have to check if member function with same name doesn't exist? That might break some code in interesting ways too. It will not be able to respect the inheritance rules as well (at least how it is implemented currently) : class A { public function plop(){} } class B extends A {} $b = new B; $b-plop = function(){}; $b-plop(); //will still call A::plop(). And if by any chance there is a way to make that work, it will still cause trouble with internal classes that checks for inherited methods at instantiation only. Looks like it would either introduce a lot of inconsistencies, or a lot of edge cases to fix by adding exceptions in the code. Regards, -- Stanislav Malyshev, Zend Software Architect s...@zend.com http://www.zend.com/ (408)253-8829 MSN: s...@zend.com -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php -- Etienne Kneuss http://www.colder.ch Men never do evil so completely and cheerfully as when they do it from a religious conviction. -- Pascal -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [RFC] prototyping
Hello Stanislav, Tuesday, January 13, 2009, 12:07:31 AM, you wrote: Hi! 1) a non static closure assigned to an instance changes the closures this to be set to the actual object: I'm not sure why would you expect this. If you have closure that has some bound $var inside, and you use it in context which has another $var, you don't expect that $var change to new scope's $var, do you? I always thought of closures as taking their variables and context with them, not changing them each time caller changes. Variables and this are two very different things in PHP and even more so for closures. Because there this does not reflect the object itself but the object it is bound to. Also, this adds very new thing to PHP - objects that change while being assigned. I am not sure it is a good thing. Well Closures are a brand new thing in PHP. So far we had nothing even remotely close to the closures we have right now. 4) Cloning an object assigns the properties correct. Right now we increase the refcount only instead of cloning as that is the default behavior of cloning. Since a normal variable splits on changes nothing ever notices this. For oject types that do not support cloning this is very different though. Now we cannot simply add cloning as then we'd further change closure behavior. And this way we would not fix the this pointer. Besides the issue above with changing $this I'm not sure what would proper clone do - i.e. by-val bound variables are by-val anyway, so it shouldn't matter if they are cloned, and by-ref ones should be connected anyway so again it doesn't matter. Am I missing something? BTW, why would one clone closures anyway? I Was not at all speaking of static variables. In fact I suggest we keep them exactly as they are. Which is probably what you want, based on what you wrote. And in my opinion it also makes the most sense. A closure keeps its state. 5) A closure assigned to a property can be called directly rather than just by a temporary variable: $obj-property = function() {} $obj-property(); // valid call This will get us into trouble IMHO, as it would not behave too well with __get/__set and __call. I.e. if we have $foo-nosuchproperty() - should we call __get or __call? So far variables implemented through __get behave exactly like ones implemented through definition - but I don't understand how to reconcile it with this. Why would you call __get() here? Becasue I did that by mistake in my very first mail? You clearly have a function call and thus you only go for __call(). As of today you can already do: function __call($name, $args) { if ($this-properties[$name] instanceof 'Closure') { return call_user_func_array($this-property[$name], $args); } } Now we already have callable properties - directly callable. I also don't like having special one-class check inside assignment operators just for this narrow function - it doesn't look like good generic code and I suspect for proper implementation may require instanceof check on each assignment - which would be really bad. No, becasue Closure cannot be derived as it is a final class. If we change that later then we simply have to change the check to test for the handler. We might want to do that anyway as it is faster. 2) The current behavior seems inconsistent as it matters where an assignment of a closure to a proeprty is being performed. OR how a closure is being created. Of course it matters how (or, more precisely, where) the closure was created - isn't it the whole point of closure? It matters how you bind static variables to it as they are taken from the context. And by the binding you keep the context. Sure all right. But we bind this in a completely different way. And when I bind a closure to an object inside that object why should it be different from an assignment outside the class? And it gets even better, if you assign that closure to another object it gets private access to the other classes members; plus you can bind it to the new classes private members as well. This is a nightmare and plain wrong to me and the part with accessing another classes private members without knowing will create a very large WTF factor and conflicts with everythign we ever said about visibility. 3) If closures get callable by property directly then we end up in a situation where we can have two methods with the same name. That means it is discussable whether we want to allow assignment of a closure to a member variable name that already exists as a private member function. This is another thing - does it mean on each assignment we'd have to check if member function with same name doesn't exist? That might break some code in interesting ways too. I don't see a reason for this. But people might differ. And at the end of the day the problems arising from this are the least evil. Best regards, Marcus -- PHP Internals - PHP Runtime Development Mailing List To
Re: [PHP-DEV] [RFC] prototyping
Hi! Also, this adds very new thing to PHP - objects that change while being assigned. I am not sure it is a good thing. Well Closures are a brand new thing in PHP. So far we had nothing even remotely close to the closures we have right now. There are a lot of different features in PHP, that's not the reason to turn the engine into a salad of special cases and exceptions. That's why making Closure an object and having __invoke was a good idea - because it fits what the engine already does very well. Having special case just for one class doesn't fit it so well. you wrote. And in my opinion it also makes the most sense. A closure keeps its state. I consider $this be a part of this state. Maybe if you really need it it'd be fine to do something like $closure-setThis($object)... Why would you call __get() here? Becasue I did that by mistake in my very Imagine such code: class Contains { private $_store; function __set($n, $v) { $this-_store[$n] = $v; } function __get($n) { return $this-_store[$n]; } } Pretty standard class. Now imagine you do this: $c = new Contains(); $c-foo = bar; echo $c-foo; works well and basically you see Contains as regular object and couldn't care less it has handlers and not properties. Now this: $c-bar = function() { } $c-bar(); what happens here? You can't get the value of bar without calling __get and if you call __call instead that means you either lost $c-bar or you have two kinds of properties now - ones that call __get and ones that don't. I don't see how it is good. No, becasue Closure cannot be derived as it is a final class. If we change Why it's a final class? Any special reason for that? It matters how you bind static variables to it as they are taken from the context. And by the binding you keep the context. Sure all right. But we bind this in a completely different way. I see no reason to have two contexts in closure - one bound one way and another bound another way. I think context is a context - it's where the closure was created. different from an assignment outside the class? And it gets even better, if you assign that closure to another object it gets private access to the other classes members; plus you can bind it to the new classes private That's the whole point of the closure - if you make object-bound closure you can pass accessors to private variables to external classes. If you don't want it - either don't access privates in your closure or make the closure static. The whole point of the closure is that you can keep the context with the closure and thus give other scope regulated access to your scope. I don't see a reason for this. But people might differ. And at the end of the day the problems arising from this are the least evil. The least between what and what? I don't see any problem or evil with what we have now except for missing exotic feature of rebinding closures - which would be a huge WTF for me if all closures worked this way (I definitely expect closure to keep its context and not switch it each time I pass it around), but if there was a special way to make it work this way (as setThis mentioned above or any other way) and it would not conflict with other features and not require dirty hacks on the engine I wouldn't mind. -- Stanislav Malyshev, Zend Software Architect s...@zend.com http://www.zend.com/ (408)253-8829 MSN: s...@zend.com -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [RFC] prototyping
just a note about it in javascript, this ($this in php) is a special variable in closure, this is always bound to the object of object.method(...) when it is called, whenever i want a this of closure creation time context, i use: var _this = this; return function() { _this.prop = 123; _this.method(); } so.. I see no reason to have two contexts in closure - one bound one way and another bound another way it's already 2 contexts in php: $this and $_LOCALSCOPE (which is not available in user code). and now $_CLOSURE context the reason you think there's only 1 context maybe because $this is implemented in $_LOCALSCOPE['this'] = $this in php internally. :) I think context is a context - it's where the closure was created. true, the $_CLOSURE context prototype can be useful in php. and like closure that ppl got used to javascript version of closure/prototype, there should be no big problem understanding it. -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
Re: [PHP-DEV] [RFC] prototyping
Hi! 1) a non static closure assigned to an instance changes the closures this to be set to the actual object: I'm not sure why would you expect this. If you have closure that has some bound $var inside, and you use it in context which has another $var, you don't expect that $var change to new scope's $var, do you? I always thought of closures as taking their variables and context with them, not changing them each time caller changes. Also, this adds very new thing to PHP - objects that change while being assigned. I am not sure it is a good thing. 4) Cloning an object assigns the properties correct. Right now we increase the refcount only instead of cloning as that is the default behavior of cloning. Since a normal variable splits on changes nothing ever notices this. For oject types that do not support cloning this is very different though. Now we cannot simply add cloning as then we'd further change closure behavior. And this way we would not fix the this pointer. Besides the issue above with changing $this I'm not sure what would proper clone do - i.e. by-val bound variables are by-val anyway, so it shouldn't matter if they are cloned, and by-ref ones should be connected anyway so again it doesn't matter. Am I missing something? BTW, why would one clone closures anyway? 5) A closure assigned to a property can be called directly rather than just by a temporary variable: $obj-property = function() {} $obj-property(); // valid call This will get us into trouble IMHO, as it would not behave too well with __get/__set and __call. I.e. if we have $foo-nosuchproperty() - should we call __get or __call? So far variables implemented through __get behave exactly like ones implemented through definition - but I don't understand how to reconcile it with this. I also don't like having special one-class check inside assignment operators just for this narrow function - it doesn't look like good generic code and I suspect for proper implementation may require instanceof check on each assignment - which would be really bad. 2) The current behavior seems inconsistent as it matters where an assignment of a closure to a proeprty is being performed. OR how a closure is being created. Of course it matters how (or, more precisely, where) the closure was created - isn't it the whole point of closure? 3) If closures get callable by property directly then we end up in a situation where we can have two methods with the same name. That means it is discussable whether we want to allow assignment of a closure to a member variable name that already exists as a private member function. This is another thing - does it mean on each assignment we'd have to check if member function with same name doesn't exist? That might break some code in interesting ways too. -- Stanislav Malyshev, Zend Software Architect s...@zend.com http://www.zend.com/ (408)253-8829 MSN: s...@zend.com -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php