Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:42616 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 82388 invoked from network); 14 Jan 2009 11:56:02 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 14 Jan 2009 11:56:02 -0000 Authentication-Results: pb1.pair.com smtp.mail=ionut.g.stan@gmail.com; spf=pass; sender-id=pass Authentication-Results: pb1.pair.com header.from=ionut.g.stan@gmail.com; sender-id=pass; domainkeys=bad Received-SPF: pass (pb1.pair.com: domain gmail.com designates 209.85.134.191 as permitted sender) DomainKey-Status: bad X-DomainKeys: Ecelerity dk_validate implementing draft-delany-domainkeys-base-01 X-PHP-List-Original-Sender: ionut.g.stan@gmail.com X-Host-Fingerprint: 209.85.134.191 mu-out-0910.google.com Received: from [209.85.134.191] ([209.85.134.191:8367] helo=mu-out-0910.google.com) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id 76/A7-37365-1D2DD694 for ; Wed, 14 Jan 2009 06:56:02 -0500 Received: by mu-out-0910.google.com with SMTP id i2so246479mue.3 for ; Wed, 14 Jan 2009 03:55:58 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:received:received:message-id:date:from :user-agent:mime-version:to:cc:subject:references:in-reply-to :content-type:content-transfer-encoding; bh=2CM+9IbpAsZcCyaKsxwSmuh31CntfUn8TuggVr7eEMg=; b=TK/XEYvkEBd6msrdQVCzuHOjIFvUGuLGNBqf5KTfMNungIZjYCR1f8u/c06FYWBlH/ 0DDSyMCXkvq/BvKLh5u8+9OmvwF+upssSy802BO/kxdZy1zNPJKuxzKvz0d8yrV67P+c sz9/paHTE73wKcpO9bg4QqEJ1B0JlnKGjFY7Y= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=message-id:date:from:user-agent:mime-version:to:cc:subject :references:in-reply-to:content-type:content-transfer-encoding; b=i9Ct3MkpTd3bONhZ8cUY8yN6RoQGV1PaPvOKh57zeGrWpMznyMqE6llC1iCXDEtwP1 NmddE2c2nVBkmQKUebrp0R2ow9bIG8gVRQI5f6RaCZzqsXvX2t3NV3TBx1Xm6caoaoDs JrSflnlo+70i7KL/RqZWLIOhw1EyfH7eshMqE= Received: by 10.103.40.5 with SMTP id s5mr11792712muj.93.1231934158634; Wed, 14 Jan 2009 03:55:58 -0800 (PST) Received: from ?10.0.0.187? (mail.cmbtravel.ro [84.234.105.22]) by mx.google.com with ESMTPS id j10sm60880497muh.57.2009.01.14.03.55.57 (version=TLSv1/SSLv3 cipher=RC4-MD5); Wed, 14 Jan 2009 03:55:58 -0800 (PST) Message-ID: <496DD269.4050701@gmail.com> Date: Wed, 14 Jan 2009 13:54:17 +0200 User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1b3pre) Gecko/20081204 Thunderbird/3.0b1 MIME-Version: 1.0 To: Stanislav Malyshev CC: Marcus Boerger , internals@lists.php.net References: <1016989528.20090112233204@marcus-boerger.de> <496BCD33.4080703@zend.com> <133861245.20090113225847@marcus-boerger.de> <496D2074.3070303@zend.com> In-Reply-To: <496D2074.3070303@zend.com> Content-Type: text/plain; charset=ISO-8859-15; format=flowed Content-Transfer-Encoding: 7bit Subject: Re: [PHP-DEV] [RFC] prototyping From: ionut.g.stan@gmail.com (Ionut Gabriel Stan) 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 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. In this particular case I think having callable properties has less to do with closures (I mean capturing context, not lambdas) and more to do with extending functionality of existing classes and objects without a need for creating additional classes. It's also about overriding existent behavior. In JavaScript, if you don't have a String.trim() function you add it to the prototype. From that moment on, all your strings have a trim() method: String.prototype.trim = function() {}; Of course, this is nice for primitive types that have some form of syntactic sugar representation, quotes in case of strings. So now I'm able to do: var trimmed = " has spaces ".trim(); This is of course not the case with PHP, but as I said, this feature has less to do with closures. >> 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.