Laruence, Hey: > Just one question, usage? why we need this? (run-time check is > slow and I can not think out of a case then we must need it) >
In practice, the run-time check shouldn't be that slow. In fact, I just did a quick micro-benchark without actually implementing caching, and it's only 8% slower than an interface check. To put that in perspective, it adds on average .387e-8 seconds per function call. And that's without caching. With caching, it should be no slower than a standard interface. In fact, it could potentially be *faster* than an interface. The primary reason is that the interface check happens at class definition time regardless of if it's used or not. A cached protocol check would only be run when it's actually hinted against. So this does have the potential to actually increase the performance of a codebase overall. So no, "run-time checks" are not really slow (they are no more expensive than compile-time checks) as long as we can cache them (still working on an effective cache now)... As a proof-of-concept, I implemented a primitive cache. Here's the results comparing an Interface check to a Protocol check and Native: Interface in 0.50202393531799 seconds, 5.0202393531799E-7 seconds per run Protocol in 0.48089909553528 seconds, 4.8089909553528E-7 seconds per run Native in 0.3850359916687 seconds, 3.850359916687E-7 seconds per run Now, Interface and protocol area always within about 2% of each other, and which is faster changes per run, so I think it's safe to say this method is not slow... > Go does that is not a reason... > I never said it was. I quoted it to show one inspiration for the idea. As far as what the use-case is, there are a few: 1. Library implementers don't need to declare a dependency for a third party library to use the interface from it. This may sound trivial, but imagine this. Right now Zend and Symfony have very similar providers for certain tasks. One of the ones that comes to mind (besides logging) is caching. If you want to use a Zend component in an Symfony app while still using caching today, you'd need to shim together an adapter to satisfy the zend cache interface with the symfony cache interface. Which means your project now depends on all three, as well as requiring the zend cache code for the sole purpose of loading the interface required by the component. That's loading a LOT of code that you don't need, just to avoid having to setup two caches... Instead, the zend component could depend on the narrow api that it needs. It only calls the ->get($key) and ->set($key, $value) methods, so create a (new?) interface with that limited API, and then type against it as a protocol. Now that interface still would need to be loaded, but the Symfony class can resolve that interface directly. Without the need to load anything from Zend cache (said caching interface doesn't need to be there) or wire anything else for you. This may seem like a triviality, but it's actually quite significant. Because... 2. It's dependency inversion applied to the type system. Think of it like this. Dependency Injection is dependency inversion, it pushes the requirement to resolve a dependency onto the creator instead of on the object itself. Protocols are also dependency inversion, it pushes the requirement to determine if a dependency is resolve onto the receiver instead of the creator of the object. It is *not* a replacement for interfaces. It's not intended to be. Instead, it's useful along side for cases when you want interoperability and duck-style-typing without the overhead of coupling the packages together via interfaces. 3. It allows avoiding interface-hell Right now, every time you want to create a polymorphic dependency, you need to create an interface for it to allow for others to resolve that with a class from a different tree. This can lead to situations where you have literally dozens of interfaces which you may or may not need. Additionally, you're put in a position by classes like PDO where you can't have a polymorphic dependency because there's no interface defined. This change allows for using a Class as the protocol, so you could type-hint against the API of a class without needing to create a separate interface for it. This wouldn't be useful for something you substitute often, but would be VERY useful for libraries which wrap dependencies that aren't changed often, but may need to be. So instead of creating a PDO wrapper just to add a series of interfaces for it, just hint against <PDO>. That way you're getting an object that looks like PDO, but doesn't need to be PDO if you don't want it to be... There are definitely more, but the first one I think is the big one. It decouples while still providing interface safety... Anthony PS: here's the code for said benchmark: interface Foo { public function foo(); } class Bar { public function foo() {} } class Baz implements Foo { public function foo() {} } function benchmark($func, $times, $arg) { $s = microtime(true); for ($i = 0; $i < $times; $i++) { $func($arg); } $e = microtime(true); return $e - $s; } $times = 1000000; $interface = benchmark(function(Foo $foo) {}, $times, new Baz); echo "Interface in $interface seconds, " . ($interface / $times) . " seconds per run\n"; $protocol = benchmark(function(<Foo> $foo) {}, $times, new Bar); echo "Protocol in $protocol seconds, " . ($protocol / $times) . " seconds per run\n"; $native = benchmark(function($foo) {}, $times, new Bar); echo "Native in $native seconds, " . ($native / $times) . " seconds per run\n";