Last active
June 19, 2016 19:22
-
-
Save hikari-no-yume/9baa7726cfefc8b21b3e to your computer and use it in GitHub Desktop.
Unbound, scoped closures, and why they would be useful
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
// A closure can be bound ($this will be a specific object), unbound ($this not set) or static ($this cannot be set) | |
// A closure may be scoped (it can see the private properties of a specific class), or unscoped (no class) | |
// At present, a closure must be bound or static if it is scoped, and unscoped if it is unbound | |
// I propose changing this to also allow scoped, unbound closures | |
// I want to add Closure::call, which lets you call and bind a closure (to avoid creating a new closure for each object you want to bind to) | |
$foo = function () { | |
return $this->x; | |
}; | |
$bar = (object)['x' => 3]; | |
$qux = (object)['x' => 4]; | |
$foo->call($bar); // returns 3 | |
$foo->call($qux); // returns 4 | |
// However, for technical reasons, Closure::call can only bind, it can't change the scope. | |
// Therefore if you need to be scoped (to see private properties etc.) and want to use ::call, you must created a scoped closure ahead-of-time | |
// However, at the moment, you cannot have an unbound yet scoped closure, only one that is bound or static | |
// If we try to do this: | |
class FooBar { | |
private $x = 0; | |
} | |
$foo = function () { | |
return $this->x; | |
}; | |
// ($newthis[, $scope]) | |
$foo->bindTo(NULL, 'FooBar'); | |
// We will get a static closure, which can see FooBar's private variables but can't be bound to anything! | |
// This means that we actually would have to bound to some dummy object first at the moment, as you can rebind a bound closure: | |
$foo->bindTo(new StdObject, 'FooBar'); | |
// However, there are some cases where you can't do this | |
// (e.g. methods on internal classes don't work with other classes, and the binding would fail) | |
// It would be better to just allow creating a closure that is scoped to a class, but isn't bound: | |
// new optional argument: $unbound_scoped | |
$foo->bindTo(NULL, 'FooBar', true); | |
// This is actually what my function references proposal does internally for methods | |
// Because it uses :: syntax it doesn't know what to bind to, but it does know the scope | |
// If we made them static, it'd be useless (you can't bind to anything!) | |
// If we didn't scope them, it'd be strange (a method should naturally be scoped to its class) | |
// Thus it has to make unbound but scoped closures | |
class FooBar { | |
private $x = 0; | |
function bar() { | |
return $this->x; | |
} | |
} | |
// Under my function refs proposal, this gets you an unbound, scoped closure | |
$foo = &FooBar::bar; | |
$qux = new FooBar; | |
$qux->x = 3; | |
$foo->call($qux); // Returns 3, as call binds it to $qux and $foo is already scoped to FooBar | |
// I hope this helped! | |
// - Andrea Faulds (@andreafaulds, [email protected]) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment