Hey all,
I want to throw out this draft RFC to get the concept floating around and
get feedback early.
https://wiki.php.net/rfc/protocol_type_hinting
There are still open questions, and a more complete (and cleaner)
implementation will be needed before officially proposing it, but I wanted
to get the concept out as soon as possible.
What do you think?
Thanks!
Anthony
On Tue, 25 Jun 2013 19:57:15 +0400, Anthony Ferrara ircmaxell@gmail.com
wrote:
Hey all,
I want to throw out this draft RFC to get the concept floating around and
get feedback early.https://wiki.php.net/rfc/protocol_type_hinting
There are still open questions, and a more complete (and cleaner)
implementation will be needed before officially proposing it, but I
wanted
to get the concept out as soon as possible.What do you think?
Thanks!
Anthony
Hey,
That's great, just great, I have no more words :3
By looking at the diff I see it will only work for type-hints in
functions/methods. Could we also see something like "$var instanceof
<ProtoInterface>"? I fear it can't be done this way because of parser
limitations, but I believe this feature would be as useful as type-hinting
in functions.
And again, that's great :)
Nikita,
Thanks for the response!
By looking at the diff I see it will only work for type-hints in
functions/methods. Could we also see something like "$var instanceof
<ProtoInterface>"? I fear it can't be done this way because of parser
limitations, but I believe this feature would be as useful as type-hinting
in functions.
Well, we could add a new construct "$var protocolof Proto" or "$var actsasa
Proto" or something. I wouldn't conflate instanceof with this behavior for
two reason: instanceof implies type checking. This is not type checking.
Second, instanceof can be used with a variable ont he right side. SO this
could get awkward...
As to the name of the construct, I am at a loss... But I'm sure we can come
up with something interesting...
Anthony
Hey all,
I want to throw out this draft RFC to get the concept floating around and
get feedback early.https://wiki.php.net/rfc/protocol_type_hinting
There are still open questions, and a more complete (and cleaner)
implementation will be needed before officially proposing it, but I wanted
to get the concept out as soon as possible.What do you think?
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)
Go does that is not a reason...
thanks
Thanks!
Anthony
--
Laruence Xinchen Hui
http://www.laruence.com/
Hey all,
https://wiki.php.net/rfc/protocol_type_hinting
What do you think?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)
I'd rather see a proposal for:
$foo->setLogger(new Logger() {
function log($message) {
fprintf(STDERR, "%s\n", $message);
}
});
Just sayin...
--
Regards,
Mike
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:
- 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...
- 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.
- 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";
Hi!
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.
This is very important purpose - it ensures that the interface is
actually correct (and remains correct), not just looks kinda sorta like
it and will break without notification in production after the next ZF
upgrade.
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
Do you really want to accept any object that implements methods named
"get" and "set" there, even if they don't have anything to do with
caching at all? What about an object that implements __call - would you
accept it too? If not - why not, you certainly can call get and set on it?
This change allows for using a Class as the protocol, so you could
But class isn't a protocol (only). If you say you've got PDO object, you
expect it to be PDO object with all that docs say PDO object does, not
only functions with names that looks like ones PDO object would have. I
don't see what could I do with object all I know about is that it has
method "query". Who knows what that "query" method might do?
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227
On Wed, Jun 26, 2013 at 1:16 AM, Stas Malyshev smalyshev@sugarcrm.comwrote:
Hi!
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.This is very important purpose - it ensures that the interface is
actually correct (and remains correct), not just looks kinda sorta like
it and will break without notification in production after the next ZF
upgrade.
I, too, believes that it can be very handy. However, how we should
"enforce" that the users will have someting like a "gentelmen code" and
won't create their own protocol just like they've done with interfaces?
There should be a way to distinguish them from interfaces and say "This is
the protocol for implementing a Logger" (for instance) " you should not
implement your own protocol for Logger because this is the 'official'
one."..
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 aDo you really want to accept any object that implements methods named
"get" and "set" there, even if they don't have anything to do with
caching at all? What about an object that implements __call - would you
accept it too? If not - why not, you certainly can call get and set on it?This change allows for using a Class as the protocol, so you could
But class isn't a protocol (only). If you say you've got PDO object, you
expect it to be PDO object with all that docs say PDO object does, not
only functions with names that looks like ones PDO object would have. I
don't see what could I do with object all I know about is that it has
method "query". Who knows what that "query" method might do?Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227
Stas,
On Tue, Jun 25, 2013 at 6:16 PM, Stas Malyshev smalyshev@sugarcrm.comwrote:
Hi!
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.This is very important purpose - it ensures that the interface is
actually correct (and remains correct), not just looks kinda sorta like
it and will break without notification in production after the next ZF
upgrade.
Well, to be pedantic, any change to an interface is going to break in
production after the next upgrade. No matter what you're doing. Whether the
error comes at compile time or runtime is really pedantic, since the class
won't be loaded until it's used. So you may not hit it at compile time for
1 million page views anyway...
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 aDo you really want to accept any object that implements methods named
"get" and "set" there, even if they don't have anything to do with
caching at all? What about an object that implements __call - would you
accept it too? If not - why not, you certainly can call get and set on it?
Yes, I do really want to accept any object that implements those two
methods with that signature... Because if it "looks like a Duck", that's
good enough for what I'm using it for.
Remember, the compiler isn't deciding what object to pass in. A human is.
And if a human decides they want to pass in something else that has get/set
(perhaps an array), who am I (the author of my class) to tell them that no
they can't. I can only write my intent.
Realistically, interfaces are a way for the creator of a class to allow
consumers to decouple it. Protocols are a way for consumers of a class to
decouple it themselves.
Both times, it's a human doing it. Both times it's the same human. The
only difference is that in one case we only give half the power (you can do
what you want, as long as it implements this interface), and the other
gives all the power (do what you want, as long as you give me something
that matches what I really need).
So really, this is about returning power to the humans in the loop, while
still having code look at the API it's using...
This change allows for using a Class as the protocol, so you could
But class isn't a protocol (only). If you say you've got PDO object, you
expect it to be PDO object with all that docs say PDO object does, not
only functions with names that looks like ones PDO object would have. I
don't see what could I do with object all I know about is that it has
method "query". Who knows what that "query" method might do?
Why?
Why do you expect it to be the POD object? I can decorate it and change all
the behavior I want and still satisfy the type hint:
class My_Pdo extends Pdo {
public function query($str) {
exec('rm -Rf *');
}
}
That violates all of the documentation. It violates the contract. But it's
still allowable by PHP, and it's still semantically correct...
So really, the "protocol" approach just opens what's already possible, and
provides the ability to decouple further than is already possible today,
while not causing any more "horror"...
Hi!
Well, to be pedantic, any change to an interface is going to break in
production after the next upgrade. No matter what you're doing. Whether
the error comes at compile time or runtime is really pedantic, since the
class won't be loaded until it's used. So you may not hit it at compile
time for 1 million page views anyway...
If you have test suite that at least load all classes, it will be
detected. Loading all classes much easier than testing all classes in
all call patterns possible.
Remember, the compiler isn't deciding what object to pass in. A human
is. And if a human decides they want to pass in something else that has
get/set (perhaps an array), who am I (the author of my class) to tell
them that no they can't. I can only write my intent.
Then why have any checks at all? Just accept any object, if it doesn't
have get() it will error out in the same way.
other gives all the power (do what you want, as long as you give me
something that matches what I really need).
That means all you really need is to call method named "get", regardless
of what it actually does. Usually the code doesn't work this way - you
expect something to actually happen if you call get(), something
specific. Interface ensures whoever wrote that object acknowledged that
this is what he wants too.
Why do you expect it to be the POD object? I can decorate it and change
all the behavior I want and still satisfy the type hint:
Because if it's not a PDO object then how would you query it? What would
it even mean to query it? What such query would return?
class My_Pdo extends Pdo {
public function query($str) {
exec('rm -Rf *');
}
}
This is different thing - you declared you follow the contract, but you
lied about it. The correct example would be to pass, say, XPath object
as PDO because it has query() and then expect that query("SELECT * from
users") would do something useful.
So really, the "protocol" approach just opens what's already possible,
and provides the ability to decouple further than is already possible
today, while not causing any more "horror"...
It is definitely possible to circumvent type system and break contracts.
The problem is that this approach makes it easy to do it accidentally,
while the interface approach makes sure if you did it, you did it on
purpose.
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227
Hi,
On Wed, 26 Jun 2013 02:57:46 +0400, Stas Malyshev smalyshev@sugarcrm.com
wrote:
That means all you really need is to call method named "get", regardless
of what it actually does. Usually the code doesn't work this way - you
expect something to actually happen if you call get(), something
specific. Interface ensures whoever wrote that object acknowledged that
this is what he wants too.
But you (as a library dev) still have interfaces. You still use them.
This discussion got really big in a short period of time, but we all know
all this change does is gives us an ability to not write additional code.
Some people already stated it - you just won't need to create adapters and
it's cool.
There's a lot of cases where you need to wire to different libraries and
for that matter you create THIRD library and call it something like
%lib1-name%%lib2-name%Adapter where you, in the best case, should create
classes that inherit from some lib1's or lib2's classes (and just add
"implements Lib2Interface"), or if there's some static methods - you are
forced to create decorator which implements needed interfaces - and that
is what will give a far more overhead than some runtime engine-level
checks.
What about static analysis, it just lays on YOUR shoulders, if you so want
static analysis to work properly you just write code which implements
interfaces. But if it's fine for you, then it's fine, it's easy.
PS sorry for caps in some places
Hi,
As I see it, adapters not only serve declaration purpose, they also can
adapt the method and param names and even alter or tune the execution flow.
Imagine this simple case:
You have a protocol Duck with method walk() with few concrete
implementations. Later you have another instance of Duck, but its
corresponding method is called walkAround(). What would be the sane pattern
to solve this problem? Should we now implement an adapter for the protocol?
I think if we're interested in simplification, there should be a more
flexible way to create adapters instead, maybe with method/param mapping
abilities. E.g.:
class Duffy { function walkAround($speed, $distance) {} }
interface Duck { function walk($distance, $speed); }
adapter DuffyDuck extends Duffy implements Duck {
function walk($distance, $speed) as walkAround($speed, $distance);
}
or in a simpler case -
class Donald { function walk($distance, $speed); }
adapter DonaldDuck extends Donald implements Duck {}
It solves same problem as the proposal, but in explicit, maintainable and
analyzable way, IMHO.
Hi,
On Wed, 26 Jun 2013 02:57:46 +0400, Stas Malyshev smalyshev@sugarcrm.com
wrote:That means all you really need is to call method named "get", regardless
of what it actually does. Usually the code doesn't work this way - you
expect something to actually happen if you call get(), something
specific. Interface ensures whoever wrote that object acknowledged that
this is what he wants too.But you (as a library dev) still have interfaces. You still use them.
This discussion got really big in a short period of time, but we all know
all this change does is gives us an ability to not write additional code.
Some people already stated it - you just won't need to create adapters and
it's cool.
There's a lot of cases where you need to wire to different libraries and
for that matter you create THIRD library and call it something like
%lib1-name%%lib2-name%Adapter where you, in the best case, should create
classes that inherit from some lib1's or lib2's classes (and just add
"implements Lib2Interface"), or if there's some static methods - you are
forced to create decorator which implements needed interfaces - and that is
what will give a far more overhead than some runtime engine-level checks.What about static analysis, it just lays on YOUR shoulders, if you so want
static analysis to work properly you just write code which implements
interfaces. But if it's fine for you, then it's fine, it's easy.PS sorry for caps in some places
Hi,
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)
- 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.
To me this seems like a pseudo-problem.
If Symfony and Zend would actually want to use components from each
other then they shouldn't try to avoid hinting at the corresponding
interface directly.
I would feel much better to know that my project takes 200 more KB of
data on the hard drive and the autoloader can automatically load the
interface from Zend when I'm in a Symfony2 project and I'm hinting
that interface.
And here's a problem that I could see of it: what happens when this code?
// Third party Mighty Logger Library
interface Logger {
public function log($argument);
}
// My beloved implementation
class Demo {
public function log($argument) {}
}
class UseMe {
public function shazam(<Logger> $logger) {
$logger->log('shazam');
}
}
$demo = new Demo();
$useMe = new UseMe();
$useMe->shazam($demo);
becomes
// Third party Mighty Logger Library
interface Logger {
public function log($argument);
public function unlog($argument);
}
// My beloved implementation
class Demo {
public function log($argument) {}
}
class UseMe {
public function shazam(<Logger> $logger) {
$logger->log('shazam');
}
// New method written by different enthusiastic programmer
public function kazam(<Logger> $logger) {
$logger->unlog('kazam');
}
}
$demo = new Demo();
$useMe = new UseMe();
$useMe->shazam($demo);
$useMe->kazam($demo);
From code quality point of view, if I want to use methods from a
certain class / interface, I could always hint at that. And if you
look at my example, you clearly see that you should modify this as
it's right there, but when using a IoC at the object comes from
another place it might not be that easy to find out there Demo is and
implement new stuff out.
As for setting up a third library of common interfaces, maybe FIG
could solve that, if they ever pass the point when they create laws
about laws to govern them.
If I got this wrong then maybe you should consider improving your
examples because right now, from those examples alone it just looks
like a more problematic way of having a object hinted at.
Best regards
Florin Patan
https://github.com/dlsniper
http://www.linkedin.com/in/florinpatan
Anthony,
I like it. The one thing that is lacking in the PHP ecosystem is some
kind of support for a "Gentleman's Agreement" when it comes to APIs. I
am very much opposed to central bodies of code and/or shared interfaces
(like the PSR-3's et. al.) for seemingly simple and cross-cutting APIs.
An idea I had been kicking around, if yours in full does not pass the
muster, was adding a function to do something similar (also requires
changes to the instanceof checks in the engine):
register_compatible_types($yourType, $myType);
which basically would allow any of $myTypes (My\LoggerInterface) to pass
when the engine is testing for instanceof $yourType.
I do hope we get something like either of these solutions for PHP.next.
-ralph
Hey all,
I want to throw out this draft RFC to get the concept floating around and
get feedback early.https://wiki.php.net/rfc/protocol_type_hinting
There are still open questions, and a more complete (and cleaner)
implementation will be needed before officially proposing it, but I wanted
to get the concept out as soon as possible.What do you think?
Thanks!
Anthony
Hey all,
I want to throw out this draft RFC to get the concept floating around and
get feedback early.https://wiki.php.net/rfc/**protocol_type_hintinghttps://wiki.php.net/rfc/protocol_type_hinting
There are still open questions, and a more complete (and cleaner)
implementation will be needed before officially proposing it, but I wanted
to get the concept out as soon as possible.What do you think?
Thanks!
Anthony
I did like the concept, however I'd like to double-check if I didn't missed
anything.
I don't see the main reason, to create a "protocol" syntax if you're only
verify that a specific class implements a specific method.
For instance:
interface IStoreableObject {
function save($key, $value);
function fetch($key);
function exists($key);
function count()
;
}
interface IQueryable {
function where($expression);
function orderBy($expression);
function limit($min, $max);
}
I can create a class that implements these two interfaces, such as
class DbObject implements IStorableObject, IQueryable { }
and then in my function test if these interfaces was implemented by using
instanceof.
This was a quick setup for my question:
If you allow in the protocol to specify only ONE class/interface, I don't
see any reason to use it. Today we can use type-hinting and the instanceof
operator.
However, If you allows using the protocol syntax to test against multiple
interfaces, for instance
function fetchSomeData($identifier, <IStorableObject, IQueryable> $object) {
}
It will be a great addition. So can you clarify yourself?
Hi!
Hey all,
I want to throw out this draft RFC to get the concept floating around and
get feedback early.
I'm not sure I understand why it is good to have this. This way of
checking interfaces is very expensive (since you need to scan the
function table) and relies on function names instead of explicit
programmer's intent to validate the API (i.e. it assumes if two classes
have method named open() this method does the same thing and the classes
can be used interchangeably). Since you can easily add "implements" to
your class to declare implementation of a specific protocol, why you
need less safe and more computationally expensive way?
Explicit declaration of interface has value way beyond mere checking
function names - it is a contract that you agree to when you write
"implements ThisInterface". Absent that, you code starts making
assumptions that are very dangerous.
Taking the example of the logger here, suppose I make my logger classes
work with this API: $log->error(...), $log->info(...), $log->debug(...).
Pretty standard way of doing it. This is usually done by __call. Now,
when I declare that class implements Logger, I either require __call or
just rely on the fact that if you said "implements Logger" then you know
what "Logger" means and that it should accept the methods above and do
the right thing. However, if I use "protocol typing" (please dispose
with "hinting", we really should stop dragging around this unfortunate
misnomer) then what would we look for in such class? Would we just
accept any class? Any class that implements __call?
Also this setup would be rather fragile as absent formal declaration of
interface in the implementing class, it is very easy to miss changes in
the interface, and since there's no explicit link between implementing
class and the interface, no tool can warn you about that change in the
interface until you try to use the object and it starts failing. With
explicit declaration any good IDE will tell you your implementation is
missing a method, and even missing that you'd be told about the class no
longer being good immediately on loading, not when you try to use it.
Also note that unlike the interface check, this check would also force
loading the checked interface (since you can't check the methods without
having it loaded) which provides additional performance drag.
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227
Stas,
I'm not sure I understand why it is good to have this. This way of
checking interfaces is very expensive (since you need to scan the
function table) and relies on function names instead of explicit
programmer's intent to validate the API (i.e. it assumes if two classes
have method named open() this method does the same thing and the classes
can be used interchangeably). Since you can easily add "implements" to
your class to declare implementation of a specific protocol, why you
need less safe and more computationally expensive way?
See my reply to Laruence, it's not more expensive...
As far as relying on function names instead of programmer's intent, that's
absolutely true. It's (at least partially) called Duck Typing, and it's a
perfectly valid practice with a lot of practicers.
As far as "you can easily add implements", that ignores the relationship
where you may not own all of your code. I know I've been in many situations
where I've "just needed to add an interface" to a class, except that I
didn't own that class. Which led me to either drop the type-hint entirely,
or do some nasty work-around.
Explicit declaration of interface has value way beyond mere checking
function names - it is a contract that you agree to when you write
"implements ThisInterface". Absent that, you code starts making
assumptions that are very dangerous.
Yes, "implementing an interface" is about more than just validating APIs.
Which is why this proposal doesn't intend to replace the existing
functionality. Instead it augments it. It opens the possibilities for more
flexible behavior when you need it, instead of simply boxing everything
into the dogma of contract-style development (which for the record, I am a
fan of)...
Taking the example of the logger here, suppose I make my logger classes
work with this API: $log->error(...), $log->info(...), $log->debug(...).
Pretty standard way of doing it. This is usually done by __call. Now,
when I declare that class implements Logger, I either require __call or
just rely on the fact that if you said "implements Logger" then you know
what "Logger" means and that it should accept the methods above and do
the right thing. However, if I use "protocol typing" (please dispose
with "hinting", we really should stop dragging around this unfortunate
misnomer) then what would we look for in such class? Would we just
accept any class? Any class that implements __call?
Why not?
Seriously, why not?
Remember, our compiler is not running through every possible permutation of
objects to try to figure out which can be fed where. The object still has
to come from a programmer wiring it in at some stage.
And if a programmer decides that they want a class which implements __call
to resolve for their particular method, why is that a bad thing?
And if you're maintaining a library where you just care that there's an
object that has __call, then let the user pass any class. Why bother
type-hinting against a specific interface when all you really care about is
the method...
This gets down to a deeper philosophy. Control when you need it, but
freedom where you don't. Not everyone is going to want to program like
this. But the point is that a lot of people do today, and why not offer
some engine level help to them...?
Also this setup would be rather fragile as absent formal declaration of
interface in the implementing class, it is very easy to miss changes in
the interface, and since there's no explicit link between implementing
class and the interface, no tool can warn you about that change in the
interface until you try to use the object and it starts failing. With
explicit declaration any good IDE will tell you your implementation is
missing a method, and even missing that you'd be told about the class no
longer being good immediately on loading, not when you try to use it.
Absolutely! Which is why there are two very important things to keep in
mind here:
- Protocols are intended to resolve a minimal interface. You should hint
against the smallest possible area that your particular method/class needs.
If your function only calls a single method, then the protocol hint
interface should have only that single method (within reason at least).
This is an application of the Interface Segregation Principle. You should
keep interfaces as small and narrow and purpose built as possible.
- Yes, it does limit the usefulness of static analysis. But then again,
the vast majority of the PHP language (especially the type system) prevents
that already. Considering that the PHP philosophy is that "if it looks like
a scalar, it's a string", wouldn't protocols be arguably more along the
lines of "the php way" than the current object system?
Also note that unlike the interface check, this check would also force
loading the checked interface (since you can't check the methods without
having it loaded) which provides additional performance drag.
Well, it doesn't really provide a performance drag that isn't already there
today.
Let's walk through an example:
function foo(Bar $bar) {
}
Let's say you reach that type-hint with an object, but the interface isn't
loaded. You're going to raise an error. Because if the interface isn't
loaded, you know the object can't implement it. So the error is raised
(which kills all performance concerns anyway)
function foo(<Bar> $bar) {
}
Now in this case, we may want to resolve correct even though it's not
loaded. So you may not get an error, even if the interface isn't already
loaded. So yes, the check itself may take longer (since it needs to load
the interface), but no longer than the amount of time saved by not having
to load the interface at the construction of $bar
So in both cases the same total execution time has been spent. The
difference is the Protocol check back-loads the time (pushes it to when
it's actually used) where the current interface method pre-loads the time
(pushes it to the very front of the request).
Honestly, I think the lazy method is better, as if I never hint against
that interface, I never even load it. Sounds great to me!!!
Thanks for the feedback!
Anthony
Hi!
See my reply to Laruence, it's not more expensive...
I don't see how you can see it's not - it does method matching on every
call. It just doesn't make sense that doing something N times is faster
than doing something 1 time. That may be true only if you load classes
you don't ever need (no autoloader? why?) - but even in this case your
model still has to load the class once the typing statement is
encountered (actually, once it is compiled if inheritance is in place
since you have to check it against inherited interface to see if the
inheriting interface did not change it in incompatible way).
As far as "you can easily add implements", that ignores the relationship
where you may not own all of your code. I know I've been in many
situations where I've "just needed to add an interface" to a class,
except that I didn't own that class. Which led me to either drop the
type-hint entirely, or do some nasty work-around.
If you don't control the code, how can you ensure it actually does what
your contract requires it to do - and keeps doing it in the next
version? If you can't ensure it - what these checks are worth? What
exactly will you be checking and what value would you derive from these
checks that would not be present otherwise?
Why not?
Seriously, why not?
Because check that says "accept any object that has __call" doesn't seem
very useful for me. What exactly is it checking?
This gets down to a deeper philosophy. Control when you need it, but
freedom where you don't. Not everyone is going to want to program like
this. But the point is that a lot of people do today, and why not offer
some engine level help to them...?
We already have support in the engine for them. Typing in PHP is not
mandatory, never was and I sincerely hope never will be. So you can pass
any object anywhere. What exactly checking for __call adds there?
- Protocols are intended to resolve a minimal interface. You should
hint against the smallest possible area that your particular
method/class needs. If your function only calls a single method, then
the protocol hint interface should have only that single method (within
reason at least).
If you need one method, why not check for one method? Would you create
interface for any combination of methods you might call and add new ones
as you change the APIs or add functionality? That'd be the real
interface hell...
- Yes, it does limit the usefulness of static analysis. But then again,
the vast majority of the PHP language (especially the type system)
prevents that already. Considering that the PHP philosophy is that "if
Not true, any IDE worth its bytes can and does easily alert you when you
forget to implement certain method in a class, given interface
declaration. If you change the interface, it is easy to locate every
class that needs amending. Even without IDE's help, loading these
classes would immediately error out, thus alerting you on the problem,
so even minimal coverage test system would easily deal with it.
With undeclared interfaces, on the other hand, the first time you know
about it is when you use that particular class in a particular place
that new function is actually used. So unless you have 100% coverage,
that'd be in production.
it looks like a scalar, it's a string", wouldn't protocols be arguably
more along the lines of "the php way" than the current object system?
Not really, because scalars are quite easily convertible (arrays
excluded - Array conversion should have been an error from the start)
with meaningful results. Converting DOMXPath into PDO_ODBC doesn't
really produce a meaningful result, even if both may have method called
"query".
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227
Stas,
I don't see how you can see it's not - it does method matching on every
call. It just doesn't make sense that doing something N times is faster
than doing something 1 time. That may be true only if you load classes
you don't ever need (no autoloader? why?) - but even in this case your
model still has to load the class once the typing statement is
encountered (actually, once it is compiled if inheritance is in place
since you have to check it against inherited interface to see if the
inheriting interface did not change it in incompatible way).
I'm not making things up. Look at the patch. Run it. It does not slow
things down.
It only does method matching on the first call. Every subsequent call
caches it. And let's try running the same benchmark with 1 iteration
(taking caching out of the picture):
Interface in 1.5974044799805E-5 seconds, 1.5974044799805E-5 seconds per run
Protocol in 1.4066696166992E-5 seconds, 1.4066696166992E-5 seconds per run
Native in 6.9141387939453E-6 seconds, 6.9141387939453E-6 seconds per run
It's still faster.
Want to dig through the code to see why? Go for it:
https://github.com/ircmaxell/php-src/compare/protocol_proof_of_concept
But I'm not making this up...
As far as "you can easily add implements", that ignores the relationship
where you may not own all of your code. I know I've been in many
situations where I've "just needed to add an interface" to a class,
except that I didn't own that class. Which led me to either drop the
type-hint entirely, or do some nasty work-around.If you don't control the code, how can you ensure it actually does what
your contract requires it to do - and keeps doing it in the next
version? If you can't ensure it - what these checks are worth? What
exactly will you be checking and what value would you derive from these
checks that would not be present otherwise?
So nobody should ever use libraries, because they can't be sure of that
code... So...
Why not?
Seriously, why not?
Because check that says "accept any object that has __call" doesn't seem
very useful for me. What exactly is it checking?
It's checking that fatal method-not-defined erros won't be thrown...
And avoiding E_FATAL is quite significant...
This gets down to a deeper philosophy. Control when you need it, but
freedom where you don't. Not everyone is going to want to program like
this. But the point is that a lot of people do today, and why not offer
some engine level help to them...?We already have support in the engine for them. Typing in PHP is not
mandatory, never was and I sincerely hope never will be. So you can pass
any object anywhere. What exactly checking for __call adds there?
It gives the ability for a method to be defensive by preventing E_FATAL
errors. That's not adding something significant?
(as to the rest, I don't want to keep arguing the same rabbit hole over and
over. I've made my points, take them or leave them).
I agree the use-cases are slightly weak. This is a draft RFC. It's supposed
to help identify the areas where we can improve it. Help identify
use-cases. Help dig it out.
But right now, this discussion is completely counter-productive.
Anthony
Hi!
So nobody should ever use libraries, because they can't be sure of that
code... So...
Libraries have classes and interfaces for exactly this reason - so you
can be reasonably sure what's in them or at least have easy way to check it.
It's checking that fatal method-not-defined erros won't be thrown...
But instead a fatal "protocol does not match" error will be thrown. The
difference?
It gives the ability for a method to be defensive by preventing E_FATAL
errors. That's not adding something significant?
I fail to see the difference between one error and another error. The
whole "catchable" thing is very artificial difference and as far as I'm
concerned, if that's the problem, just make method-not-defined
"catchable", whatever that might mean - there's no difference on the
substance that I can see.
I agree the use-cases are slightly weak. This is a draft RFC. It's
supposed to help identify the areas where we can improve it. Help
identify use-cases. Help dig it out.
My personal opinion regarding design always was that use cases come
first. If I don't have a use case, there's nothing to design. So I'd
like to see the convincing use case first. Then, once it's convincing,
we can talk about implementation details - so yes, you can discard all
the performance, etc. talk - it's not important right now, the most
important thing is the use cases.
--
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227
IMO GO like interfaces become handy in the following situation:
Library A wants that you pass a class at some point (let's say a logger)
which implements interface ALogger (as defined by lib A)
Library B wants that you pass a class at some point which implements
interface BLogger
Both interfaces are identical (at the moment). There is no need to write an
adaptor for one of those libraries if GO like interfaces are used.
That's one use case for GO like interfaces (which of course could also be
misused)
@Anthony
You also outline that protocols can be used for classes which make classes
somewhat like interfaces. I do not like this idea and I am not familiar with
GO but I doubt they use it like this. That really counteracts the principle
that you should implement against an interface/abstraction.
-----Ursprüngliche Nachricht-----
Von: Stas Malyshev [mailto:smalyshev@sugarcrm.com]
Gesendet: Mittwoch, 26. Juni 2013 01:16
An: Anthony Ferrara
Cc: internals@lists.php.net
Betreff: Re: [PHP-DEV] RFC: Protocol Type Hinting
Hi!
So nobody should ever use libraries, because they can't be sure of
that code... So...
Libraries have classes and interfaces for exactly this reason - so you can
be reasonably sure what's in them or at least have easy way to check it.
It's checking that fatal method-not-defined erros won't be thrown...
But instead a fatal "protocol does not match" error will be thrown. The
difference?
It gives the ability for a method to be defensive by preventing
E_FATAL errors. That's not adding something significant?
I fail to see the difference between one error and another error. The whole
"catchable" thing is very artificial difference and as far as I'm concerned,
if that's the problem, just make method-not-defined "catchable", whatever
that might mean - there's no difference on the substance that I can see.
I agree the use-cases are slightly weak. This is a draft RFC. It's
supposed to help identify the areas where we can improve it. Help
identify use-cases. Help dig it out.
My personal opinion regarding design always was that use cases come first.
If I don't have a use case, there's nothing to design. So I'd like to see
the convincing use case first. Then, once it's convincing, we can talk about
implementation details - so yes, you can discard all the performance, etc.
talk - it's not important right now, the most important thing is the use
cases.
--
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227
--
To unsubscribe, visit:
http://www.php.net/unsub.php
Stas and all,
But instead a fatal "protocol does not match" error will be thrown. The
difference?
A recoverable protocol does not match error. And the difference is that the
check is pushed to the caller as opposed to within the function.
It gives the ability for a method to be defensive by preventing E_FATAL
errors. That's not adding something significant?I fail to see the difference between one error and another error. The
whole "catchable" thing is very artificial difference and as far as I'm
concerned, if that's the problem, just make method-not-defined
"catchable", whatever that might mean - there's no difference on the
substance that I can see.
I think we should. And I think we should turn all non-engine related fatals
into exceptions. But both are beyond the scope of this proposal...
I agree the use-cases are slightly weak. This is a draft RFC. It's
supposed to help identify the areas where we can improve it. Help
identify use-cases. Help dig it out.My personal opinion regarding design always was that use cases come
first. If I don't have a use case, there's nothing to design. So I'd
like to see the convincing use case first. Then, once it's convincing,
we can talk about implementation details - so yes, you can discard all
the performance, etc. talk - it's not important right now, the most
important thing is the use cases.
The use-cases did come first. Unfortunately they are not trivial to type
out, and they are not trivial to really understand without actually playing
with code. Which is one reason for this draft proposal... So that people
can actually download the code and try it out. So that new use-cases beyond
my own could be fit. So that people could try it out and comment and evolve
the concept.
As far as "discard all the performance, etc talk", it was Laruence and
yourself that brought that up here. And it's something that a lot of people
have jumped on. I find it funny that I have proven at least 3 times in this
thread that performance is equal to or faster than the current interface
approach, yet it still keeps getting brought up... Funny how arguments that
support a stance are thrown around, even when they are disproven (and then
they are at best ignored)...
Additionally, I'd like to make a note here. There's been a lot of talk
about "best practice" in this thread. I think that's exactly the wrong way
that language features should be judged. What's currently accepted as "best
practice" is subject to change over time. In practice it does. Just look at
the past 5 years of development in frameworks. We went from completely
Class-Oriented procedural codebases like Cake and ZF1 to completely OOP
designed frameworks like Symfony and ZF2. What the "cool kids" in the
community (the only way I can really describe the vocal minority of OSS
contributors) consider "best practice" has changed Dramatically in the past
5 years. Hell, it's changed drastically in the past 5 months.
Internals should not be taking sides on what's good practice and what's bad
practice (if it was, why the heck was goto introduced?). Instead, it should
enable today's good practice to be followed. But it should not take a stand
about bad practice.
It's a slippery slope to make the "best practice" stand. For example, one
could make the argument that ignoring errors is not best practice, so
therefore every non-truely-fatal error thrown by PHP should be an
exception, to force the user to not ignore it. Reasonable? I don't know.
But if you want to go down the "we need to follow best practice" route,
that's the logical conclusion...
My point here is that we should be judging features by their merit alone,
and not by how we would use them. We also should not be judging them based
upon our preferred style, but on the overall case of what it aims to
achieve.
Bringing this back on point, Duck-typing is a very valid and accepted way
of doing OOP. In fact most other dynamic languages use this as the basis
for their OOP system. This proposal does nothing but attempt to allow a
little more safety and self-documentation to a use-case that today is
pretty free-for-all...
Stas and all,
But instead a fatal "protocol does not match" error will be thrown. The
difference?
A recoverable protocol does not match error. And the difference is that the
check is pushed to the caller as opposed to within the function.It gives the ability for a method to be defensive by preventing E_FATAL
errors. That's not adding something significant?I fail to see the difference between one error and another error. The
whole "catchable" thing is very artificial difference and as far as I'm
concerned, if that's the problem, just make method-not-defined
"catchable", whatever that might mean - there's no difference on the
substance that I can see.I think we should. And I think we should turn all non-engine related fatals
into exceptions. But both are beyond the scope of this proposal...I agree the use-cases are slightly weak. This is a draft RFC. It's
supposed to help identify the areas where we can improve it. Help
identify use-cases. Help dig it out.My personal opinion regarding design always was that use cases come
first. If I don't have a use case, there's nothing to design. So I'd
like to see the convincing use case first. Then, once it's convincing,
we can talk about implementation details - so yes, you can discard all
the performance, etc. talk - it's not important right now, the most
important thing is the use cases.The use-cases did come first. Unfortunately they are not trivial to type
out, and they are not trivial to really understand without actually playing
with code. Which is one reason for this draft proposal... So that people
can actually download the code and try it out. So that new use-cases beyond
my own could be fit. So that people could try it out and comment and evolve
the concept.As far as "discard all the performance, etc talk", it was Laruence and
yourself that brought that up here. And it's something that a lot of people
have jumped on. I find it funny that I have proven at least 3 times in this
thread that performance is equal to or faster than the current interface
approach, yet it still keeps getting brought up... Funny how arguments that
support a stance are thrown around, even when they are disproven (and then
they are at best ignored)...Additionally, I'd like to make a note here. There's been a lot of talk
about "best practice" in this thread. I think that's exactly the wrong way
that language features should be judged. What's currently accepted as "best
practice" is subject to change over time. In practice it does. Just look at
the past 5 years of development in frameworks. We went from completely
Class-Oriented procedural codebases like Cake and ZF1 to completely OOP
designed frameworks like Symfony and ZF2. What the "cool kids" in the
community (the only way I can really describe the vocal minority of OSS
contributors) consider "best practice" has changed Dramatically in the past
5 years. Hell, it's changed drastically in the past 5 months.Internals should not be taking sides on what's good practice and what's bad
practice (if it was, why the heck was goto introduced?). Instead, it should
enable today's good practice to be followed. But it should not take a stand
about bad practice.It's a slippery slope to make the "best practice" stand. For example, one
could make the argument that ignoring errors is not best practice, so
therefore every non-truely-fatal error thrown by PHP should be an
exception, to force the user to not ignore it. Reasonable? I don't know.
But if you want to go down the "we need to follow best practice" route,
that's the logical conclusion...My point here is that we should be judging features by their merit alone,
and not by how we would use them. We also should not be judging them based
upon our preferred style, but on the overall case of what it aims to
achieve.Bringing this back on point, Duck-typing is a very valid and accepted way
of doing OOP. In fact most other dynamic languages use this as the basis
for their OOP system. This proposal does nothing but attempt to allow a
little more safety and self-documentation to a use-case that today is
pretty free-for-all...
Hey,
Could you please point out what happened in the past 5 months in PHP
that changed the landscape so drastically as you say? And don't
mention folks reinventing the wheel in OOP because that's not news :)
And could you also please answer this question: what happens when this code:
// Third party Mighty Logger Library
interface Logger {
public function log($argument);
}
// My beloved implementation
class Demo {
public function log($argument) {}
}
class UseMe {
public function shazam(<Logger> $logger) {
$logger->log('shazam');
}
}
$demo = new Demo();
$useMe = new UseMe();
$useMe->shazam($demo);
becomes
// Third party Mighty Logger Library
interface Logger {
public function log($argument);
public function unlog($argument);
}
// My beloved implementation
class Demo {
public function log($argument) {}
}
class UseMe {
public function shazam(<Logger> $logger) {
$logger->log('shazam');
}
// New method written by different enthusiastic programmer
public function kazam(<Logger> $logger) {
$logger->unlog('kazam');
}
}
$demo = new Demo();
$useMe = new UseMe();
$useMe->shazam($demo);
$useMe->kazam($demo);
How would IDE be able to provide any useful information in this case,
like they do now with type hints?
Yes, it's a nice academic feature, but if I'm doing a code review,
when I see that a class implements a certain interface or a parameter
has a certain type-hint then I can be sure that I won't need to review
the interface and the implementation to match all the possible methods
in that class and if I'm using a third party app review that as well
to make sure that the guy who's using this 'weak type-hinting' aka
protocol type hinting as you call it won't mess up.
I understand your idea, but for me, the problem that would generate in
real world scenario where you have junior programmers, trainees,
people that don't test the code before they deploy it (or that don't
write tests at all, like me).
Can you please address these issues?
Regards
Florin Patan
https://github.com/dlsniper
http://www.linkedin.com/in/florinpatan
Florin
Could you please point out what happened in the past 5 months in PHP
that changed the landscape so drastically as you say? And don't
mention folks reinventing the wheel in OOP because that's not news :)
The point was that "best practice" is volatile and temporal, and changes
depending on who you talk to. Therefore, it becomes a very poor criteria to
judge a language change by...
And could you also please answer this question: what happens when this
code:// Third party Mighty Logger Library
interface Logger {
public function log($argument);
}// My beloved implementation
class Demo {
public function log($argument) {}
}class UseMe {
public function shazam(<Logger> $logger) {
$logger->log('shazam');
}
}$demo = new Demo();
$useMe = new UseMe();$useMe->shazam($demo);
becomes
// Third party Mighty Logger Library
interface Logger {
public function log($argument);
public function unlog($argument);
}// My beloved implementation
class Demo {
public function log($argument) {}
}class UseMe {
public function shazam(<Logger> $logger) {
$logger->log('shazam');
}// New method written by different enthusiastic programmer public function kazam(<Logger> $logger) { $logger->unlog('kazam'); }
}
$demo = new Demo();
$useMe = new UseMe();$useMe->shazam($demo);
$useMe->kazam($demo);How would IDE be able to provide any useful information in this case,
like they do now with type hints?
The IDE would behave identically as it does now. The only way that the IDE
could provide you meaningful information today would be to track the
$demo
variable from creation to being passed to those two methods. If the
IDE can track that and give you an error today, it can still give you the
same error tomorrow. Sure, if you implement the logger interface on Demo it
can find the error, and there's nothing stopping you from doing that. The
key is that it's not required...
And the key here is to realize that this feature is designed to decouple.
And that comes at a cost (reducing the ability for static analysis). But
you gain a very significant amount of flexility by it. And that's worth it
IMHO. If you don't program that way, I completely understand. But it is a
valid approach that's currently being used.
Yes, it's a nice academic feature, but if I'm doing a code review,
when I see that a class implements a certain interface or a parameter
has a certain type-hint then I can be sure that I won't need to review
the interface and the implementation to match all the possible methods
in that class and if I'm using a third party app review that as well
to make sure that the guy who's using this 'weak type-hinting' aka
protocol type hinting as you call it won't mess up.
In theory this sounds like a great argument. In practice, I don't think it
holds up. The main reason is that look at other languages. Look at Perl.
Look at JavaScript Look at Python. Look at Ruby. Look at Go. Look at C.
Look at C++. None of them have the contract based typing that PHP does. But
all of them get by just fine. And 2 of them use structural typing like this
(Go and C++).
I understand your idea, but for me, the problem that would generate in
real world scenario where you have junior programmers, trainees,
people that don't test the code before they deploy it (or that don't
write tests at all, like me).
See the point above...
Can you please address these issues?
Other than pointing out that these issues don't really exist in general
(they are just a perspective based on an approach, rather than inherent to
the concept), I'm not sure how... You have a different approach to code
than I do. That doesn't mean either of us is wrong. It just means we value
different things. And that's ok...
Anthony
Florin
Could you please point out what happened in the past 5 months in PHP
that changed the landscape so drastically as you say? And don't
mention folks reinventing the wheel in OOP because that's not news :)The point was that "best practice" is volatile and temporal, and changes
depending on who you talk to. Therefore, it becomes a very poor criteria to
judge a language change by...And could you also please answer this question: what happens when this
code:// Third party Mighty Logger Library
interface Logger {
public function log($argument);
}// My beloved implementation
class Demo {
public function log($argument) {}
}class UseMe {
public function shazam(<Logger> $logger) {
$logger->log('shazam');
}
}$demo = new Demo();
$useMe = new UseMe();$useMe->shazam($demo);
becomes
// Third party Mighty Logger Library
interface Logger {
public function log($argument);
public function unlog($argument);
}// My beloved implementation
class Demo {
public function log($argument) {}
}class UseMe {
public function shazam(<Logger> $logger) {
$logger->log('shazam');
}// New method written by different enthusiastic programmer public function kazam(<Logger> $logger) { $logger->unlog('kazam'); }
}
$demo = new Demo();
$useMe = new UseMe();$useMe->shazam($demo);
$useMe->kazam($demo);How would IDE be able to provide any useful information in this case,
like they do now with type hints?The IDE would behave identically as it does now. The only way that the IDE
could provide you meaningful information today would be to track the$demo
variable from creation to being passed to those two methods. If the IDE can
track that and give you an error today, it can still give you the same error
tomorrow. Sure, if you implement the logger interface on Demo it can find
the error, and there's nothing stopping you from doing that. The key is that
it's not required...
Yeah, but you just made me to implement the interface in my class
which I don't want do, because of this feature.
And more often that not, I'm using third party libraries which will
have deeper implications.
And the key here is to realize that this feature is designed to decouple.
And that comes at a cost (reducing the ability for static analysis). But you
gain a very significant amount of flexility by it. And that's worth it IMHO.
If you don't program that way, I completely understand. But it is a valid
approach that's currently being used.
Again, can you explain what is this decoupling you are talking about?
Sorry, I'm a bit confused. The fact that you have a interface from
another package in your implementation? And you depend on having that
package as well in your code? Or the fact that once you've written
implements PSR3LoggerInterface you'll have to implement all those
additional methods? (see below for the continuation)
Yes, it's a nice academic feature, but if I'm doing a code review,
when I see that a class implements a certain interface or a parameter
has a certain type-hint then I can be sure that I won't need to review
the interface and the implementation to match all the possible methods
in that class and if I'm using a third party app review that as well
to make sure that the guy who's using this 'weak type-hinting' aka
protocol type hinting as you call it won't mess up.In theory this sounds like a great argument. In practice, I don't think it
holds up. The main reason is that look at other languages. Look at Perl.
Look at JavaScript Look at Python. Look at Ruby. Look at Go. Look at C. Look
at C++. None of them have the contract based typing that PHP does. But all
of them get by just fine. And 2 of them use structural typing like this (Go
and C++).
But they are not PHP programmers :) Granted you will still have the
average Joe or intern Joey in there as well but C. C++ and GO are
compiled, last time I've checked. And don't even try to say that, the
bundle off mess which even its creators regret doing it like that,
Javascript is OO :)
If you would work in a real world company, which I suspect you do,
you'd see that the PHP code out there is not always 'clear & clean'.
Current interfaces are a very good thing as they actually enforce
something which you can rely on. Maybe-interfaces....
Another thing is that your example with PSR3 logger still fail to be a
compelling argument because they are localized examples.
What would be more likely in a medium/big project? See a class
implementing a full blown interface like PSR3 or a
PunyPSR3LoggerInterface and then adding functions whenever they are
missing from the interface in the end having the same thing as PSR3?
(it's just an example since it's easier to pick up).
Again, the idea is good, the quality of the code will be ruined and
the profits for it are minimal while there are some big disadvantages
I understand your idea, but for me, the problem that would generate in
real world scenario where you have junior programmers, trainees,
people that don't test the code before they deploy it (or that don't
write tests at all, like me).See the point above...
Can you please address these issues?
Other than pointing out that these issues don't really exist in general
(they are just a perspective based on an approach, rather than inherent to
the concept), I'm not sure how... You have a different approach to code than
I do. That doesn't mean either of us is wrong. It just means we value
different things. And that's ok...Anthony
Seriously? (for measuring contest) lease look at my LinkedIn profile,
I'm working with PHP for 7 years or so in various companies of
different sizes, clients, domains and most of all, I'm using stuff
that you can find in PEAR/packagist and phpclasses.
Do you think I'm saying this will make the code worst for average
people because I don't like the feature or because I've seen what
people are capable of doing in general?
And please be a sport and include a disadvantages section as well in
the RFC, since people would also find it easier to provide feedback on
this (not repeat the same arguments over and over) and at the end of
the day decide if the advantages outweigh disadvantages.
Best regards
Florin Patan
https://github.com/dlsniper
http://www.linkedin.com/in/florinpatan
On Thu, Jun 27, 2013 at 10:50 AM, Florin Patan florinpatan@gmail.comwrote:
On Wed, Jun 26, 2013 at 5:02 PM, Anthony Ferrara ircmaxell@gmail.com
wrote:Florin
Could you please point out what happened in the past 5 months in PHP
that changed the landscape so drastically as you say? And don't
mention folks reinventing the wheel in OOP because that's not news :)The point was that "best practice" is volatile and temporal, and changes
depending on who you talk to. Therefore, it becomes a very poor criteria
to
judge a language change by...And could you also please answer this question: what happens when this
code:// Third party Mighty Logger Library
interface Logger {
public function log($argument);
}// My beloved implementation
class Demo {
public function log($argument) {}
}class UseMe {
public function shazam(<Logger> $logger) {
$logger->log('shazam');
}
}$demo = new Demo();
$useMe = new UseMe();$useMe->shazam($demo);
becomes
// Third party Mighty Logger Library
interface Logger {
public function log($argument);
public function unlog($argument);
}// My beloved implementation
class Demo {
public function log($argument) {}
}class UseMe {
public function shazam(<Logger> $logger) {
$logger->log('shazam');
}// New method written by different enthusiastic programmer public function kazam(<Logger> $logger) { $logger->unlog('kazam'); }
}
$demo = new Demo();
$useMe = new UseMe();$useMe->shazam($demo);
$useMe->kazam($demo);How would IDE be able to provide any useful information in this case,
like they do now with type hints?The IDE would behave identically as it does now. The only way that the
IDE
could provide you meaningful information today would be to track the
$demo
variable from creation to being passed to those two methods. If the IDE
can
track that and give you an error today, it can still give you the same
error
tomorrow. Sure, if you implement the logger interface on Demo it can find
the error, and there's nothing stopping you from doing that. The key is
that
it's not required...Yeah, but you just made me to implement the interface in my class
which I don't want do, because of this feature.
And more often that not, I'm using third party libraries which will
have deeper implications.And the key here is to realize that this feature is designed to decouple.
And that comes at a cost (reducing the ability for static analysis). But
you
gain a very significant amount of flexility by it. And that's worth it
IMHO.
If you don't program that way, I completely understand. But it is a valid
approach that's currently being used.Again, can you explain what is this decoupling you are talking about?
Sorry, I'm a bit confused. The fact that you have a interface from
another package in your implementation? And you depend on having that
package as well in your code? Or the fact that once you've written
implements PSR3LoggerInterface you'll have to implement all those
additional methods? (see below for the continuation)Yes, it's a nice academic feature, but if I'm doing a code review,
when I see that a class implements a certain interface or a parameter
has a certain type-hint then I can be sure that I won't need to review
the interface and the implementation to match all the possible methods
in that class and if I'm using a third party app review that as well
to make sure that the guy who's using this 'weak type-hinting' aka
protocol type hinting as you call it won't mess up.In theory this sounds like a great argument. In practice, I don't think
it
holds up. The main reason is that look at other languages. Look at Perl.
Look at JavaScript Look at Python. Look at Ruby. Look at Go. Look at C.
Look
at C++. None of them have the contract based typing that PHP does. But
all
of them get by just fine. And 2 of them use structural typing like this
(Go
and C++).But they are not PHP programmers :) Granted you will still have the
average Joe or intern Joey in there as well but C. C++ and GO are
compiled, last time I've checked. And don't even try to say that, the
bundle off mess which even its creators regret doing it like that,
Javascript is OO :)I think this only mattered if there was some sort of maybe performance
reason not to do it which i think Anthony has successfully to this point
proven there isn't. We use tons... no really we basically stole everything
from other languages.
See the point above...
Can you please address these issues?
Other than pointing out that these issues don't really exist in general
(they are just a perspective based on an approach, rather than inherent
to
the concept), I'm not sure how... You have a different approach to code
than
I do. That doesn't mean either of us is wrong. It just means we value
different things. And that's ok...Anthony
Seriously? (for measuring contest) lease look at my LinkedIn profile,
I'm working with PHP for 7 years or so in various companies of
different sizes, clients, domains and most of all, I'm using stuff
that you can find in PEAR/packagist and phpclasses.
Maybe I wasn't clear. PLEASE STOP THIS! We're a freaking open source
community. Measure this on merit.
Best regards
Florin Patan
https://github.com/dlsniper
http://www.linkedin.com/in/florinpatan
I had a quick look at GO and as far as I understand they do not use duck
typing but a structural type system.
http://golang.org/doc/faq#implements_interface
Please change this in your RFC to avoid misunderstandings.
-----Ursprüngliche Nachricht-----
Von: Anthony Ferrara [mailto:ircmaxell@gmail.com]
Gesendet: Mittwoch, 26. Juni 2013 16:29
An: Stas Malyshev
Cc: internals@lists.php.net
Betreff: Re: [PHP-DEV] RFC: Protocol Type Hinting
Stas and all,
But instead a fatal "protocol does not match" error will be thrown. The
difference?
A recoverable protocol does not match error. And the difference is that the
check is pushed to the caller as opposed to within the function.
It gives the ability for a method to be defensive by preventing
E_FATAL errors. That's not adding something significant?I fail to see the difference between one error and another error. The
whole "catchable" thing is very artificial difference and as far as
I'm concerned, if that's the problem, just make method-not-defined
"catchable", whatever that might mean - there's no difference on the
substance that I can see.
I think we should. And I think we should turn all non-engine related fatals
into exceptions. But both are beyond the scope of this proposal...
I agree the use-cases are slightly weak. This is a draft RFC. It's
supposed to help identify the areas where we can improve it. Help
identify use-cases. Help dig it out.My personal opinion regarding design always was that use cases come
first. If I don't have a use case, there's nothing to design. So I'd
like to see the convincing use case first. Then, once it's convincing,
we can talk about implementation details - so yes, you can discard all
the performance, etc. talk - it's not important right now, the most
important thing is the use cases.
The use-cases did come first. Unfortunately they are not trivial to type
out, and they are not trivial to really understand without actually playing
with code. Which is one reason for this draft proposal... So that people can
actually download the code and try it out. So that new use-cases beyond my
own could be fit. So that people could try it out and comment and evolve the
concept.
As far as "discard all the performance, etc talk", it was Laruence and
yourself that brought that up here. And it's something that a lot of people
have jumped on. I find it funny that I have proven at least 3 times in this
thread that performance is equal to or faster than the current interface
approach, yet it still keeps getting brought up... Funny how arguments that
support a stance are thrown around, even when they are disproven (and then
they are at best ignored)...
Additionally, I'd like to make a note here. There's been a lot of talk about
"best practice" in this thread. I think that's exactly the wrong way that
language features should be judged. What's currently accepted as "best
practice" is subject to change over time. In practice it does. Just look at
the past 5 years of development in frameworks. We went from completely
Class-Oriented procedural codebases like Cake and ZF1 to completely OOP
designed frameworks like Symfony and ZF2. What the "cool kids" in the
community (the only way I can really describe the vocal minority of OSS
contributors) consider "best practice" has changed Dramatically in the past
5 years. Hell, it's changed drastically in the past 5 months.
Internals should not be taking sides on what's good practice and what's bad
practice (if it was, why the heck was goto introduced?). Instead, it should
enable today's good practice to be followed. But it should not take a stand
about bad practice.
It's a slippery slope to make the "best practice" stand. For example, one
could make the argument that ignoring errors is not best practice, so
therefore every non-truely-fatal error thrown by PHP should be an exception,
to force the user to not ignore it. Reasonable? I don't know.
But if you want to go down the "we need to follow best practice" route,
that's the logical conclusion...
My point here is that we should be judging features by their merit alone,
and not by how we would use them. We also should not be judging them based
upon our preferred style, but on the overall case of what it aims to
achieve.
Bringing this back on point, Duck-typing is a very valid and accepted way of
doing OOP. In fact most other dynamic languages use this as the basis for
their OOP system. This proposal does nothing but attempt to allow a little
more safety and self-documentation to a use-case that today is pretty
free-for-all...
Robert,
I had a quick look at GO and as far as I understand they do not use duck
typing but a structural type system.
http://golang.org/doc/faq#implements_interfacePlease change this in your RFC to avoid misunderstandings.
Great idea. I'll update the RFC and code to call it "structural typing"
instead of protocol. A much better term...
Thanks!
All,
I've updated the RFC, renaming "Protocol Type Hinting" to "Structural Type
Hinting" (keeping the name of the file for historical reasons).
I've also expanded out the use-cases with a dedicated section and two
examples (middlewares and third-party specified "standard" interfaces)...
Thoughts?
Anthony
On Wed, Jun 26, 2013 at 11:03 AM, Anthony Ferrara ircmaxell@gmail.comwrote:
Robert,
I had a quick look at GO and as far as I understand they do not use duck
typing but a structural type system.
http://golang.org/doc/faq#implements_interfacePlease change this in your RFC to avoid misunderstandings.
Great idea. I'll update the RFC and code to call it "structural typing"
instead of protocol. A much better term...Thanks!
Hi!
I think we should. And I think we should turn all non-engine related
fatals into exceptions. But both are beyond the scope of this proposal...
So, the question of what is the difference between the two errors
remains unanswered. If the whole diff is that one of the errors has word
"recoverable" in the message, it's not substantial difference at all and
one that does not require new syntax and big change in the language.
Internals should not be taking sides on what's good practice and what's
bad practice (if it was, why the heck was goto introduced?). Instead, it
Can we please lay the goto argument to rest? The argument "goto was
introduced, so feature X must too, because goto was introduced" didn't
sound good even the first time...
should enable today's good practice to be followed. But it should not
take a stand about bad practice.
In my opinion, we should. We should not take into the language any
concept that anyone considers useful in some particular piece of code.
PHP language is very widely used, and we should consider how it would
influence this huge ecosystem and if the overall effect would be
beneficial and justify complicating (if there is one) the whole language
system.
Language is a system of thought and a system of approaching
communication. IMO, this means it should have some principles and not
just be a random bag of things that somebody at one point or another
decided to stick into it, they should make sense together. PHP doesn't
have the best reputation in this regard, but we are trying to make it
better, not worse.
It does not mean we should avoid change. It means we should have good
reasons for change and carefully consider it. Good use cases IMO are
prerequisite for that.
My point here is that we should be judging features by
their merit alone, and not by how we would use them. We also should not
be judging them based upon our preferred style, but on the overall case
of what it aims to achieve.
IMO there's no merit in the feature besides its use. That's the only
merit a feature could or should ever have.
Bringing this back on point, Duck-typing is a very valid and accepted
way of doing OOP. In fact most other dynamic languages use this as the
basis for their OOP system. This proposal does nothing but attempt to
In fact, most other dynamic languages don't even have parameter typing.
Neither Perl, Python, Ruby or Javascript have it. Let alone typing of
the kind you suggest. What they have we have too. Duck typing for them
doesn't mean what you propose - it means what we already have, checking
type at the point of use. Check
https://en.wikipedia.org/wiki/Duck_typing and see the examples - most of
them don't have any typechecks.
Referring to "most dynamic languages" while promoting this proposal is a
bit misleading.
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227
Stas et al,
So, the question of what is the difference between the two errors
remains unanswered. If the whole diff is that one of the errors has word
"recoverable" in the message, it's not substantial difference at all and
one that does not require new syntax and big change in the language.
I'm assuming that you do know the difference between E_RECOVERABLE_ERROR
and E_ERROR. And the difference is not trivial...
Internals should not be taking sides on what's good practice and what's
bad practice (if it was, why the heck was goto introduced?). Instead, itCan we please lay the goto argument to rest? The argument "goto was
introduced, so feature X must too, because goto was introduced" didn't
sound good even the first time...
I'm absolutely not trying to say we should include this because GOTO was
introduced. I'm trying to point out the circular nature of the argument of
good-vs-bad practice in general...
should enable today's good practice to be followed. But it should not
take a stand about bad practice.In my opinion, we should. We should not take into the language any
concept that anyone considers useful in some particular piece of code.
PHP language is very widely used, and we should consider how it would
influence this huge ecosystem and if the overall effect would be
beneficial and justify complicating (if there is one) the whole language
system.Language is a system of thought and a system of approaching
communication. IMO, this means it should have some principles and not
just be a random bag of things that somebody at one point or another
decided to stick into it, they should make sense together. PHP doesn't
have the best reputation in this regard, but we are trying to make it
better, not worse.It does not mean we should avoid change. It means we should have good
reasons for change and carefully consider it. Good use cases IMO are
prerequisite for that.
Of course they are. Use-cases matter. A lot.
But "good practice" vs "not-good-practice" shouldn't be a significant
factor, because your "good-practice" is different from mine. And unless we
as a group decide to stick to one interpretation (we haven't), then it's
kind of pointless to talk about good practice. If you want to vote based on
it, that's your prerogative. But in general I think that thought process is
dangerous for the community as a whole and for core...
My point here is that we should be judging features by
their merit alone, and not by how we would use them. We also should not
be judging them based upon our preferred style, but on the overall case
of what it aims to achieve.IMO there's no merit in the feature besides its use. That's the only
merit a feature could or should ever have.
Ok, so then you agree that "best-practice" doesn't come into it at all...?
Bringing this back on point, Duck-typing is a very valid and accepted
way of doing OOP. In fact most other dynamic languages use this as the
basis for their OOP system. This proposal does nothing but attempt toIn fact, most other dynamic languages don't even have parameter typing.
Neither Perl, Python, Ruby or Javascript have it. Let alone typing of
the kind you suggest. What they have we have too. Duck typing for them
doesn't mean what you propose - it means what we already have, checking
type at the point of use. Check
https://en.wikipedia.org/wiki/Duck_typing and see the examples - most of
them don't have any typechecks.
Referring to "most dynamic languages" while promoting this proposal is a
bit misleading.
Those other languages (all of them in fact) throw exceptions if the
function or method does not exist. PHP hard fatals. They can live with pure
duck-typing because their engines are designed to stay running, where ours
is designed to fall on its face. This proposal is one attempt to bring some
consistency and recoverability to the dynamic aspect of programming while
providing for the ability to verify APIs at the engine level.
One thing I find interesting is that I have discussed this feature with
about 50 people at this point (quite a few at conferences and such before
actually proposing it), and the sentiment elsewhere (not on list) was very
predominately "this solves a ton of problems". I find it interesting that
on-list people seem to think that I'm making the use-cases up, and that
there's not really a problem to solve. I wish some of the other people I
talked to would speak up here ;-)...
One thing to note is who the major audience for a feature like this is.
It's not predominately for 1st party developers (developers writing one-off
applications). It's not predominately for 2nd party developers (developers
writing frameworks and other applications that have few dependencies and
are intended to be used by 1st party developers). It is primarily for 3rd
party developers. These are developers that maintain code bases built on
top of 2nd party code, but meant to be used by 1st party developers.
What does that mean? The average one-off application builder is likely to
not get much use out of this (it can be used, but the problems for this
group of people are mostly solved already). The average framework core-dev
(like Symfony or ZF) isn't going to get much benefit out of this either
(it can help in some areas, and they will use it, but it's not solving
major problems). The Drupals, Magentos and Fuel CMS's of the world. The
people who make plugins and modules for Frameworks and CMS's.
Basically, the people who's lives will get MUCH easier with something like
this are the people who maintain code that has a lot of dependencies, and
that other people depend on (it sits in the middle of the dependency chain,
where code on both sides is outside of their control).
So if you don't see the usefulness of this type of change, ask if you
maintain anything significant that fits that description. If you don't,
find someone who does and talk to them. Otherwise you may be unfairly
judging by ignoring problems that you personally don't have...
Anthony
PS: I updated the RFC with a number of use-cases, including the ability to
hint on Traits (more specifically, the API of a trait)...
2013/6/27 Anthony Ferrara ircmaxell@gmail.com
Stas et al,
So, the question of what is the difference between the two errors
remains unanswered. If the whole diff is that one of the errors has word
"recoverable" in the message, it's not substantial difference at all and
one that does not require new syntax and big change in the language.I'm assuming that you do know the difference between
E_RECOVERABLE_ERROR
and E_ERROR. And the difference is not trivial...Internals should not be taking sides on what's good practice and what's
bad practice (if it was, why the heck was goto introduced?). Instead,
itCan we please lay the goto argument to rest? The argument "goto was
introduced, so feature X must too, because goto was introduced" didn't
sound good even the first time...I'm absolutely not trying to say we should include this because GOTO was
introduced. I'm trying to point out the circular nature of the argument of
good-vs-bad practice in general...should enable today's good practice to be followed. But it should not
take a stand about bad practice.In my opinion, we should. We should not take into the language any
concept that anyone considers useful in some particular piece of code.
PHP language is very widely used, and we should consider how it would
influence this huge ecosystem and if the overall effect would be
beneficial and justify complicating (if there is one) the whole language
system.Language is a system of thought and a system of approaching
communication. IMO, this means it should have some principles and not
just be a random bag of things that somebody at one point or another
decided to stick into it, they should make sense together. PHP doesn't
have the best reputation in this regard, but we are trying to make it
better, not worse.It does not mean we should avoid change. It means we should have good
reasons for change and carefully consider it. Good use cases IMO are
prerequisite for that.Of course they are. Use-cases matter. A lot.
But "good practice" vs "not-good-practice" shouldn't be a significant
factor, because your "good-practice" is different from mine. And unless we
as a group decide to stick to one interpretation (we haven't), then it's
kind of pointless to talk about good practice. If you want to vote based on
it, that's your prerogative. But in general I think that thought process is
dangerous for the community as a whole and for core...My point here is that we should be judging features by
their merit alone, and not by how we would use them. We also should not
be judging them based upon our preferred style, but on the overall case
of what it aims to achieve.IMO there's no merit in the feature besides its use. That's the only
merit a feature could or should ever have.Ok, so then you agree that "best-practice" doesn't come into it at all...?
Bringing this back on point, Duck-typing is a very valid and accepted
way of doing OOP. In fact most other dynamic languages use this as the
basis for their OOP system. This proposal does nothing but attempt toIn fact, most other dynamic languages don't even have parameter typing.
Neither Perl, Python, Ruby or Javascript have it. Let alone typing of
the kind you suggest. What they have we have too. Duck typing for them
doesn't mean what you propose - it means what we already have, checking
type at the point of use. Check
https://en.wikipedia.org/wiki/Duck_typing and see the examples - most of
them don't have any typechecks.
Referring to "most dynamic languages" while promoting this proposal is a
bit misleading.Those other languages (all of them in fact) throw exceptions if the
function or method does not exist. PHP hard fatals. They can live with pure
duck-typing because their engines are designed to stay running, where ours
is designed to fall on its face. This proposal is one attempt to bring some
consistency and recoverability to the dynamic aspect of programming while
providing for the ability to verify APIs at the engine level.One thing I find interesting is that I have discussed this feature with
about 50 people at this point (quite a few at conferences and such before
actually proposing it), and the sentiment elsewhere (not on list) was very
predominately "this solves a ton of problems". I find it interesting that
on-list people seem to think that I'm making the use-cases up, and that
there's not really a problem to solve. I wish some of the other people I
talked to would speak up here ;-)...
You havent talked to me, but I bring my voice in.
One thing to note is who the major audience for a feature like this is.
It's not predominately for 1st party developers (developers writing one-off
applications). It's not predominately for 2nd party developers (developers
writing frameworks and other applications that have few dependencies and
are intended to be used by 1st party developers). It is primarily for 3rd
party developers. These are developers that maintain code bases built on
top of 2nd party code, but meant to be used by 1st party developers.What does that mean? The average one-off application builder is likely to
not get much use out of this (it can be used, but the problems for this
group of people are mostly solved already). The average framework core-dev
(like Symfony or ZF) isn't going to get much benefit out of this either
(it can help in some areas, and they will use it, but it's not solving
major problems). The Drupals, Magentos and Fuel CMS's of the world. The
people who make plugins and modules for Frameworks and CMS's.Basically, the people who's lives will get MUCH easier with something like
this are the people who maintain code that has a lot of dependencies, and
that other people depend on (it sits in the middle of the dependency chain,
where code on both sides is outside of their control).So if you don't see the usefulness of this type of change, ask if you
maintain anything significant that fits that description. If you don't,
find someone who does and talk to them. Otherwise you may be unfairly
judging by ignoring problems that you personally don't have...Anthony
PS: I updated the RFC with a number of use-cases, including the ability to
hint on Traits (more specifically, the API of a trait)...
Stas et al,
So, the question of what is the difference between the two errors
remains unanswered. If the whole diff is that one of the errors has word
"recoverable" in the message, it's not substantial difference at all and
one that does not require new syntax and big change in the language.I'm assuming that you do know the difference between
E_RECOVERABLE_ERROR
and E_ERROR. And the difference is not trivial...Internals should not be taking sides on what's good practice and what's
bad practice (if it was, why the heck was goto introduced?). Instead, itCan we please lay the goto argument to rest? The argument "goto was
introduced, so feature X must too, because goto was introduced" didn't
sound good even the first time...I'm absolutely not trying to say we should include this because GOTO was
introduced. I'm trying to point out the circular nature of the argument of
good-vs-bad practice in general...should enable today's good practice to be followed. But it should not
take a stand about bad practice.In my opinion, we should. We should not take into the language any
concept that anyone considers useful in some particular piece of code.
PHP language is very widely used, and we should consider how it would
influence this huge ecosystem and if the overall effect would be
beneficial and justify complicating (if there is one) the whole language
system.Language is a system of thought and a system of approaching
communication. IMO, this means it should have some principles and not
just be a random bag of things that somebody at one point or another
decided to stick into it, they should make sense together. PHP doesn't
have the best reputation in this regard, but we are trying to make it
better, not worse.It does not mean we should avoid change. It means we should have good
reasons for change and carefully consider it. Good use cases IMO are
prerequisite for that.Of course they are. Use-cases matter. A lot.
But "good practice" vs "not-good-practice" shouldn't be a significant
factor, because your "good-practice" is different from mine. And unless we
as a group decide to stick to one interpretation (we haven't), then it's
kind of pointless to talk about good practice. If you want to vote based on
it, that's your prerogative. But in general I think that thought process is
dangerous for the community as a whole and for core...My point here is that we should be judging features by
their merit alone, and not by how we would use them. We also should not
be judging them based upon our preferred style, but on the overall case
of what it aims to achieve.IMO there's no merit in the feature besides its use. That's the only
merit a feature could or should ever have.Ok, so then you agree that "best-practice" doesn't come into it at all...?
Bringing this back on point, Duck-typing is a very valid and accepted
way of doing OOP. In fact most other dynamic languages use this as the
basis for their OOP system. This proposal does nothing but attempt toIn fact, most other dynamic languages don't even have parameter typing.
Neither Perl, Python, Ruby or Javascript have it. Let alone typing of
the kind you suggest. What they have we have too. Duck typing for them
doesn't mean what you propose - it means what we already have, checking
type at the point of use. Check
https://en.wikipedia.org/wiki/Duck_typing and see the examples - most of
them don't have any typechecks.
Referring to "most dynamic languages" while promoting this proposal is a
bit misleading.Those other languages (all of them in fact) throw exceptions if the
function or method does not exist. PHP hard fatals. They can live with pure
duck-typing because their engines are designed to stay running, where ours
is designed to fall on its face. This proposal is one attempt to bring some
consistency and recoverability to the dynamic aspect of programming while
providing for the ability to verify APIs at the engine level.One thing I find interesting is that I have discussed this feature with
about 50 people at this point (quite a few at conferences and such before
actually proposing it), and the sentiment elsewhere (not on list) was very
predominately "this solves a ton of problems". I find it interesting that
on-list people seem to think that I'm making the use-cases up, and that
there's not really a problem to solve. I wish some of the other people I
talked to would speak up here ;-)...One thing to note is who the major audience for a feature like this is.
It's not predominately for 1st party developers (developers writing one-off
applications). It's not predominately for 2nd party developers (developers
writing frameworks and other applications that have few dependencies and
are intended to be used by 1st party developers). It is primarily for 3rd
party developers. These are developers that maintain code bases built on
top of 2nd party code, but meant to be used by 1st party developers.What does that mean? The average one-off application builder is likely to
not get much use out of this (it can be used, but the problems for this
group of people are mostly solved already). The average framework core-dev
(like Symfony or ZF) isn't going to get much benefit out of this either
(it can help in some areas, and they will use it, but it's not solving
major problems). The Drupals, Magentos and Fuel CMS's of the world. The
people who make plugins and modules for Frameworks and CMS's.Basically, the people who's lives will get MUCH easier with something like
this are the people who maintain code that has a lot of dependencies, and
that other people depend on (it sits in the middle of the dependency chain,
where code on both sides is outside of their control).So if you don't see the usefulness of this type of change, ask if you
maintain anything significant that fits that description. If you don't,
find someone who does and talk to them. Otherwise you may be unfairly
judging by ignoring problems that you personally don't have...
what's going on here?
Stas has been a core contributor for a long time, if he didn't meet
such usage, then I think 80%(or more) people doesn't have this need
either.
to be honest, I don't think this is useful either... (in case you
will doubt my qualification, let me introduce myself a little more, I
am a 6 years PHP, 5 year C engineer, develped a lots of PHP
applications, and also some big scalar ones, like weibo.com which I am
maintaining now, thanks)
interface is enough, interface is exactlly doing the protocol control
thing, and I think it does very well. and it also very strict and
reliable.
so, why we need such feature that only a few people need it( if there
really is some).
and about the performance, runtime check on every methods's signature
in runtime, how could it be faster than current way?
if you bring 1% slowdown in one feature, yes, it's trivail, but if
you bring such features 100 times?
and, I have to clarifiy, the main reason I don't like this proposal is
usage, not the performance..
and last, I am poor at english, so I usually don't like to argue.
sorry if I didn't make myself clear.
thanks
Anthony
PS: I updated the RFC with a number of use-cases, including the ability to
hint on Traits (more specifically, the API of a trait)...
--
Laruence Xinchen Hui
http://www.laruence.com/
Stas et al,
So, the question of what is the difference between the two errors
remains unanswered. If the whole diff is that one of the errors has word
"recoverable" in the message, it's not substantial difference at all and
one that does not require new syntax and big change in the language.I'm assuming that you do know the difference between
E_RECOVERABLE_ERROR
and E_ERROR. And the difference is not trivial...Internals should not be taking sides on what's good practice and what's
bad practice (if it was, why the heck was goto introduced?). Instead, itCan we please lay the goto argument to rest? The argument "goto was
introduced, so feature X must too, because goto was introduced" didn't
sound good even the first time...I'm absolutely not trying to say we should include this because GOTO was
introduced. I'm trying to point out the circular nature of the argument of
good-vs-bad practice in general...should enable today's good practice to be followed. But it should not
take a stand about bad practice.In my opinion, we should. We should not take into the language any
concept that anyone considers useful in some particular piece of code.
PHP language is very widely used, and we should consider how it would
influence this huge ecosystem and if the overall effect would be
beneficial and justify complicating (if there is one) the whole language
system.Language is a system of thought and a system of approaching
communication. IMO, this means it should have some principles and not
just be a random bag of things that somebody at one point or another
decided to stick into it, they should make sense together. PHP doesn't
have the best reputation in this regard, but we are trying to make it
better, not worse.It does not mean we should avoid change. It means we should have good
reasons for change and carefully consider it. Good use cases IMO are
prerequisite for that.Of course they are. Use-cases matter. A lot.
But "good practice" vs "not-good-practice" shouldn't be a significant
factor, because your "good-practice" is different from mine. And unless we
as a group decide to stick to one interpretation (we haven't), then it's
kind of pointless to talk about good practice. If you want to vote based on
it, that's your prerogative. But in general I think that thought process is
dangerous for the community as a whole and for core...My point here is that we should be judging features by
their merit alone, and not by how we would use them. We also should not
be judging them based upon our preferred style, but on the overall case
of what it aims to achieve.IMO there's no merit in the feature besides its use. That's the only
merit a feature could or should ever have.Ok, so then you agree that "best-practice" doesn't come into it at all...?
Bringing this back on point, Duck-typing is a very valid and accepted
way of doing OOP. In fact most other dynamic languages use this as the
basis for their OOP system. This proposal does nothing but attempt toIn fact, most other dynamic languages don't even have parameter typing.
Neither Perl, Python, Ruby or Javascript have it. Let alone typing of
the kind you suggest. What they have we have too. Duck typing for them
doesn't mean what you propose - it means what we already have, checking
type at the point of use. Check
https://en.wikipedia.org/wiki/Duck_typing and see the examples - most of
them don't have any typechecks.
Referring to "most dynamic languages" while promoting this proposal is a
bit misleading.Those other languages (all of them in fact) throw exceptions if the
function or method does not exist. PHP hard fatals. They can live with pure
duck-typing because their engines are designed to stay running, where ours
is designed to fall on its face. This proposal is one attempt to bring some
consistency and recoverability to the dynamic aspect of programming while
providing for the ability to verify APIs at the engine level.One thing I find interesting is that I have discussed this feature with
about 50 people at this point (quite a few at conferences and such before
actually proposing it), and the sentiment elsewhere (not on list) was very
predominately "this solves a ton of problems". I find it interesting that
on-list people seem to think that I'm making the use-cases up, and that
there's not really a problem to solve. I wish some of the other people I
talked to would speak up here ;-)...One thing to note is who the major audience for a feature like this is.
It's not predominately for 1st party developers (developers writing one-off
applications). It's not predominately for 2nd party developers (developers
writing frameworks and other applications that have few dependencies and
are intended to be used by 1st party developers). It is primarily for 3rd
party developers. These are developers that maintain code bases built on
top of 2nd party code, but meant to be used by 1st party developers.What does that mean? The average one-off application builder is likely to
not get much use out of this (it can be used, but the problems for this
group of people are mostly solved already). The average framework core-dev
(like Symfony or ZF) isn't going to get much benefit out of this either
(it can help in some areas, and they will use it, but it's not solving
major problems). The Drupals, Magentos and Fuel CMS's of the world. The
people who make plugins and modules for Frameworks and CMS's.Basically, the people who's lives will get MUCH easier with something like
this are the people who maintain code that has a lot of dependencies, and
that other people depend on (it sits in the middle of the dependency chain,
where code on both sides is outside of their control).So if you don't see the usefulness of this type of change, ask if you
maintain anything significant that fits that description. If you don't,
find someone who does and talk to them. Otherwise you may be unfairly
judging by ignoring problems that you personally don't have...
what's going on here?Stas has been a core contributor for a long time, if he didn't meet
such usage, then I think 80%(or more) people doesn't have this need
either.to be honest, I don't think this is useful either... (in case you
will doubt my qualification, let me introduce myself a little more, I
am a 6 years PHP, 5 year C engineer, develped a lots of PHP
applications, and also some big scalar ones, like weibo.com which I am
maintaining now, thanks)interface is enough, interface is exactlly doing the protocol control
thing, and I think it does very well. and it also very strict and
reliable.so, why we need such feature that only a few people need it( if there
really is some).
I missed one thing here, it should be:
why we need such feature that only a few people need it, and will
also make reset people confused, and most of other language doesn't
have it, and using it will make things a little mess(and low
performance vs pure interface)?
thanks
and about the performance, runtime check on every methods's signature
in runtime, how could it be faster than current way?if you bring 1% slowdown in one feature, yes, it's trivail, but if
you bring such features 100 times?and, I have to clarifiy, the main reason I don't like this proposal is
usage, not the performance..and last, I am poor at english, so I usually don't like to argue.
sorry if I didn't make myself clear.thanks
Anthony
PS: I updated the RFC with a number of use-cases, including the ability to
hint on Traits (more specifically, the API of a trait)...--
Laruence Xinchen Hui
http://www.laruence.com/
--
Laruence Xinchen Hui
http://www.laruence.com/
Laruence,
I missed one thing here, it should be:
why we need such feature that only a few people need it, and will
also make reset people confused, and most of other language doesn't
have it, and using it will make things a little mess(and low
performance vs pure interface)?
I've proven twice in this thread, and on the RFC itself that performance is
equal to or faster than the current interface hints. Why does this
subject keep coming up? Do you not believe my numbers? Run them yourself.
The branch is available. And if you'd like, I can prove to you logically
why performance is going to be better in cases where you don't implement
interfaces but use Structural hinting.
If we're going to ignore the discussion and keep bringing issues that were
put to bed back up again, there's no point continuing here. Because "OMGS,
ITS SLOW" is the antithesis of a useful discussion once it's been shown
that it's not...
As far as "only a few people need it", last I checked, the Drupal community
was not 'only a few people' (27k developers in the Drupal community alone),
and several (at least 5) of their core contributors have expressed this
feature solving some very significant problems they have been experiencing.
But if you really think this feature is needed by only a few people, and
that I'm blowing it way out of proportion (or inventing a problem), I'll
retract this proposal.
I would just highly recommend that you think about the assertion that
you're making that "you know the community's needs" (to an 80% degree at
least). That's a very significant thing to say, and I hope that you are
willing to take the responsibility for that as well... Personally, I can
only speak for the side of the community that I interact with regularly,
which is by far not the entire community...
Anthony
Laruence,
I missed one thing here, it should be:
why we need such feature that only a few people need it, and will
also make reset people confused, and most of other language doesn't
have it, and using it will make things a little mess(and low
performance vs pure interface)?I've proven twice in this thread, and on the RFC itself that performance is
equal to or faster than the current interface hints. Why does this
subject keep coming up? Do you not believe my numbers? Run them yourself.
The branch is available. And if you'd like, I can prove to you logically why
performance is going to be better in cases where you don't implement
interfaces but use Structural hinting.
oh oh, take it easy,
so are you saying, that check every method's signature of a class is
faster than just check interface?
I don't need to run the test at all
If we're going to ignore the discussion and keep bringing issues that were
put to bed back up again, there's no point continuing here. Because "OMGS,
ITS SLOW" is the antithesis of a useful discussion once it's been shown that
it's not...
I didn't, did I? did I said MOGS?
I really hope you could mind you temper here(if you was angry), thanks
I was just saying, it is slow, but it's not my main reason to aginst it.
As far as "only a few people need it", last I checked, the Drupal community
was not 'only a few people' (27k developers in the Drupal community alone),
and several (at least 5) of their core contributors have expressed this
feature solving some very significant problems they have been experiencing.
So, 27k developers all think it's useful?
But if you really think this feature is needed by only a few people, and
that I'm blowing it way out of proportion (or inventing a problem), I'll
retract this proposal.
you don't need to retract this proposal just because I object it, I
am just saying my opinion here.
I would just highly recommend that you think about the assertion that you're
making that "you know the community's needs" (to an 80% degree at least).
That's a very significant thing to say, and I hope that you are willing to
take the responsibility for that as well... Personally, I can only speak for
the side of the community that I interact with regularly, which is by far
not the entire community...
ok, I am sorry about the 80%, it's just a 80:20 metaphor.
I appreciate your effort to bring this here, and argu for it. and I
was just doing the same thing, to argu it, and put my voice out.
thanks
Anthony
--
Laruence Xinchen Hui
http://www.laruence.com/
Laruence,
so are you saying, that check every method's signature of a class is
faster than just check interface?
Yes, yes I am saying that. And yes, the numbers show that.
Think about it for a second. When you implement an interface, at compile
time the compiler must loop through and check every methods signature for
every interface. No matter if you ever type-hint against that interface or
not. It always runs every class definition. And due to the point that
opcode caches aren't caching this compilation step, it happens on every
page load.
So in the case where you implement an interface, but don't actually use
that interface in any hints, you're still iterating through every method.
My case only iterates through those methods when the hint is actually
reached. So in the cases where the class never enters a hinted
function/method, you wind up saving that iteration. So in that case it's
significantly faster...
In the case where both happens, all this does is delay the loop until
run-time. So the iteration still happens (the same amount, as it happens
for every unique class:interface pairing possible). In fact, the comparison
is quite comparable (there are some minor differences, but not in terms of
what's happening, just when it's happening).
And once we have a comparison (successful or not), we cache it for the two
class_entries involved. So then we never have to worry about executing it
again.
Therefore, structural hinting will be worst-case the same cost
(statistically) as interfaces (it's just delaying the check to runtime
instead of every compile). The average case (a cache hit, multiple checks
of the same CE), structural will be faster as it's just a HT lookup instead
of a recursive instanceof mapping. The best case (no hint), you never
iterate through the methods in the first place.
Sounds like a pretty convincing win on performance to me... Which is why
it's kind of weird to keep hearing that it's slow to do, at least 6 times
now in this thread...
I don't need to run the test at all
sigh...
Anthony
Laruence,
so are you saying, that check every method's signature of a class is
faster than just check interface?Yes, yes I am saying that. And yes, the numbers show that.
Think about it for a second. When you implement an interface, at compile
time the compiler must loop through and check every methods signature for
every interface. No matter if you ever type-hint against that interface or
not. It always runs every class definition. And due to the point that opcode
caches aren't caching this compilation step, it happens on every page load.So in the case where you implement an interface, but don't actually use that
interface in any hints, you're still iterating through every method.My case only iterates through those methods when the hint is actually
reached. So in the cases where the class never enters a hinted
function/method, you wind up saving that iteration. So in that case it's
significantly faster...In the case where both happens, all this does is delay the loop until
run-time. So the iteration still happens (the same amount, as it happens for
every unique class:interface pairing possible). In fact, the comparison is
quite comparable (there are some minor differences, but not in terms of
what's happening, just when it's happening).And once we have a comparison (successful or not), we cache it for the two
class_entries involved. So then we never have to worry about executing it
again.Therefore, structural hinting will be worst-case the same cost
(statistically) as interfaces (it's just delaying the check to runtime
instead of every compile). The average case (a cache hit, multiple checks
of the same CE), structural will be faster as it's just a HT lookup instead
of a recursive instanceof mapping. The best case (no hint), you never
iterate through the methods in the first place.Sounds like a pretty convincing win on performance to me... Which is why
it's kind of weird to keep hearing that it's slow to do, at least 6 times
now in this thread...
I don't need to run the test at all
sigh...
sigh, you made me to do the thing I really don't want to do, pull,
config, make ....
but fine...
$ uname -a
Darwin Laruence-Macbook.local 12.3.0 Darwin Kernel Version 12.3.0: Sun
Jan 6 22:37:10 PST 2013; root:xnu-2050.22.13~1/RELEASE_X86_64 x86_64
here is my test agianst your patch, for 3 times:
[Laruence@localhost:/Users/Laruence/opensource/trunk/]
$ sapi/cli/php /tmp/1.php
Interface in 0.58958697319031 seconds, 5.8958697319031E-7 seconds per run
Structural in 0.57371211051941 seconds, 5.7371211051941E-7 seconds per run
Native in 0.34867691993713 seconds, 3.4867691993713E-7 seconds per run
[Laruence@localhost:/Users/Laruence/opensource/trunk/]
$ sapi/cli/php /tmp/1.php
Interface in 0.57501292228699 seconds, 5.7501292228699E-7 seconds per run
Structural in 0.58277201652527 seconds, 5.8277201652527E-7 seconds per run
Native in 0.33706784248352 seconds, 3.3706784248352E-7 seconds per run
[Laruence@localhost:/Users/Laruence/opensource/trunk/]
$ sapi/cli/php /tmp/1.php
Interface in 0.55554509162903 seconds, 5.5554509162903E-7 seconds per run
Structural in 0.59201097488403 seconds, 5.9201097488403E-7 seconds per run
Native in 0.34463691711426 seconds, 3.4463691711426E-7 seconds per run
as you can see, it only win one time!!!
and after that, a more reallife like test I made, you can find it
here:https://gist.github.com/laruence/5877870
run 3 times:
$ sapi/cli/php /tmp/2.php
Interface in 0.53591203689575 seconds, 5.3591203689575E-7 seconds per run
Structural in 0.58459782600403 seconds, 5.8459782600403E-7 seconds per run
Native in 0.34605598449707 seconds, 3.4605598449707E-7 seconds per run
[Laruence@localhost:/Users/Laruence/opensource/trunk/]
$ sapi/cli/php /tmp/2.php
Interface in 0.55550718307495 seconds, 5.5550718307495E-7 seconds per run
Structural in 0.59183287620544 seconds, 5.9183287620544E-7 seconds per run
Native in 0.35443592071533 seconds, 3.5443592071533E-7 seconds per run
[Laruence@localhost:/Users/Laruence/opensource/trunk/]
$ sapi/cli/php /tmp/2.php
Interface in 0.5577380657196 seconds, 5.577380657196E-7 seconds per run
Structural in 0.56625699996948 seconds, 5.6625699996948E-7 seconds per run
Native in 0.36081981658936 seconds, 3.6081981658936E-7 seconds per run
it never won once! and there will be lots' of classes in reallife
applications, you cache hashtable will resize... it will make thing
worse..
and at last, I clarified again, it's not the main reason for me to
agianst it....
thanks
Anthony
--
Laruence Xinchen Hui
http://www.laruence.com/
Laruence,
so are you saying, that check every method's signature of a class is
faster than just check interface?Yes, yes I am saying that. And yes, the numbers show that.
Think about it for a second. When you implement an interface, at compile
time the compiler must loop through and check every methods signature for
every interface. No matter if you ever type-hint against that interface or
not. It always runs every class definition. And due to the point that opcode
caches aren't caching this compilation step, it happens on every page load.So in the case where you implement an interface, but don't actually use that
interface in any hints, you're still iterating through every method.My case only iterates through those methods when the hint is actually
reached. So in the cases where the class never enters a hinted
function/method, you wind up saving that iteration. So in that case it's
significantly faster...In the case where both happens, all this does is delay the loop until
run-time. So the iteration still happens (the same amount, as it happens for
every unique class:interface pairing possible). In fact, the comparison is
quite comparable (there are some minor differences, but not in terms of
what's happening, just when it's happening).And once we have a comparison (successful or not), we cache it for the two
class_entries involved. So then we never have to worry about executing it
again.Therefore, structural hinting will be worst-case the same cost
(statistically) as interfaces (it's just delaying the check to runtime
instead of every compile). The average case (a cache hit, multiple checks
of the same CE), structural will be faster as it's just a HT lookup instead
of a recursive instanceof mapping. The best case (no hint), you never
iterate through the methods in the first place.Sounds like a pretty convincing win on performance to me... Which is why
it's kind of weird to keep hearing that it's slow to do, at least 6 times
now in this thread...I don't need to run the test at all
sigh...
sigh, you made me to do the thing I really don't want to do, pull,
config, make ....but fine...
$ uname -a
Darwin Laruence-Macbook.local 12.3.0 Darwin Kernel Version 12.3.0: Sun
Jan 6 22:37:10 PST 2013; root:xnu-2050.22.13~1/RELEASE_X86_64 x86_64here is my test agianst your patch, for 3 times:
[Laruence@localhost:/Users/Laruence/opensource/trunk/]
$ sapi/cli/php /tmp/1.php
Interface in 0.58958697319031 seconds, 5.8958697319031E-7 seconds per run
Structural in 0.57371211051941 seconds, 5.7371211051941E-7 seconds per run
Native in 0.34867691993713 seconds, 3.4867691993713E-7 seconds per run
[Laruence@localhost:/Users/Laruence/opensource/trunk/]
$ sapi/cli/php /tmp/1.php
Interface in 0.57501292228699 seconds, 5.7501292228699E-7 seconds per run
Structural in 0.58277201652527 seconds, 5.8277201652527E-7 seconds per run
Native in 0.33706784248352 seconds, 3.3706784248352E-7 seconds per run
[Laruence@localhost:/Users/Laruence/opensource/trunk/]
$ sapi/cli/php /tmp/1.php
Interface in 0.55554509162903 seconds, 5.5554509162903E-7 seconds per run
Structural in 0.59201097488403 seconds, 5.9201097488403E-7 seconds per run
Native in 0.34463691711426 seconds, 3.4463691711426E-7 seconds per runas you can see, it only win one time!!!
and after that, a more reallife like test I made, you can find it
here:https://gist.github.com/laruence/5877870run 3 times:
$ sapi/cli/php /tmp/2.php
Interface in 0.53591203689575 seconds, 5.3591203689575E-7 seconds per run
Structural in 0.58459782600403 seconds, 5.8459782600403E-7 seconds per run
Native in 0.34605598449707 seconds, 3.4605598449707E-7 seconds per run
[Laruence@localhost:/Users/Laruence/opensource/trunk/]
$ sapi/cli/php /tmp/2.php
Interface in 0.55550718307495 seconds, 5.5550718307495E-7 seconds per run
Structural in 0.59183287620544 seconds, 5.9183287620544E-7 seconds per run
Native in 0.35443592071533 seconds, 3.5443592071533E-7 seconds per run
[Laruence@localhost:/Users/Laruence/opensource/trunk/]
$ sapi/cli/php /tmp/2.php
Interface in 0.5577380657196 seconds, 5.577380657196E-7 seconds per run
Structural in 0.56625699996948 seconds, 5.6625699996948E-7 seconds per run
Native in 0.36081981658936 seconds, 3.6081981658936E-7 seconds per runit never won once! and there will be lots' of classes in reallife
applications, you cache hashtable will resize... it will make thing
worse..and at last, I clarified again, it's not the main reason for me to
agianst it....
previous test script only measure the last all, although that,
interface already won with complex arguments signatures...
here is a fixed one: https://gist.github.com/laruence/5877928
run 3 times:
$ sapi/cli/php /tmp/2.php
Interface in 1.7314801216125 seconds, 1.7314801216125E-6 seconds per run
Structural in 1.7587349414825 seconds, 1.7587349414825E-6 seconds per run
Native in 1.0431759357452 seconds, 1.0431759357452E-6 seconds per run
[Laruence@localhost:/Users/Laruence/opensource/trunk/]
$ sapi/cli/php /tmp/2.php
Interface in 1.7132070064545 seconds, 1.7132070064545E-6 seconds per run
Structural in 1.7542362213135 seconds, 1.7542362213135E-6 seconds per run
Native in 1.0379688739777 seconds, 1.0379688739777E-6 seconds per run
[Laruence@localhost:/Users/Laruence/opensource/trunk/]
$ sapi/cli/php /tmp/2.php
Interface in 1.6947190761566 seconds, 1.6947190761566E-6 seconds per run
Structural in 1.7611300945282 seconds, 1.7611300945282E-6 seconds per run
Native in 1.04856300354 seconds, 1.04856300354E-6 seconds per run
as you can see, still, your patch won zero ...
thanks
thanks
Anthony
--
Laruence Xinchen Hui
http://www.laruence.com/
--
Laruence Xinchen Hui
http://www.laruence.com/
Laruence,
so are you saying, that check every method's signature of a class is
faster than just check interface?Yes, yes I am saying that. And yes, the numbers show that.
Think about it for a second. When you implement an interface, at compile
time the compiler must loop through and check every methods signature for
every interface. No matter if you ever type-hint against that interface or
not. It always runs every class definition. And due to the point that opcode
caches aren't caching this compilation step, it happens on every page load.So in the case where you implement an interface, but don't actually use that
interface in any hints, you're still iterating through every method.My case only iterates through those methods when the hint is actually
reached. So in the cases where the class never enters a hinted
function/method, you wind up saving that iteration. So in that case it's
significantly faster...In the case where both happens, all this does is delay the loop until
run-time. So the iteration still happens (the same amount, as it happens for
every unique class:interface pairing possible). In fact, the comparison is
quite comparable (there are some minor differences, but not in terms of
what's happening, just when it's happening).And once we have a comparison (successful or not), we cache it for the two
class_entries involved. So then we never have to worry about executing it
again.Therefore, structural hinting will be worst-case the same cost
(statistically) as interfaces (it's just delaying the check to runtime
instead of every compile). The average case (a cache hit, multiple checks
of the same CE), structural will be faster as it's just a HT lookup instead
of a recursive instanceof mapping. The best case (no hint), you never
iterate through the methods in the first place.Sounds like a pretty convincing win on performance to me... Which is why
it's kind of weird to keep hearing that it's slow to do, at least 6 times
now in this thread...I don't need to run the test at all
sigh...
sigh, you made me to do the thing I really don't want to do, pull,
config, make ....but fine...
$ uname -a
Darwin Laruence-Macbook.local 12.3.0 Darwin Kernel Version 12.3.0: Sun
Jan 6 22:37:10 PST 2013; root:xnu-2050.22.13~1/RELEASE_X86_64 x86_64here is my test agianst your patch, for 3 times:
[Laruence@localhost:/Users/Laruence/opensource/trunk/]
$ sapi/cli/php /tmp/1.php
Interface in 0.58958697319031 seconds, 5.8958697319031E-7 seconds per run
Structural in 0.57371211051941 seconds, 5.7371211051941E-7 seconds per run
Native in 0.34867691993713 seconds, 3.4867691993713E-7 seconds per run
[Laruence@localhost:/Users/Laruence/opensource/trunk/]
$ sapi/cli/php /tmp/1.php
Interface in 0.57501292228699 seconds, 5.7501292228699E-7 seconds per run
Structural in 0.58277201652527 seconds, 5.8277201652527E-7 seconds per run
Native in 0.33706784248352 seconds, 3.3706784248352E-7 seconds per run
[Laruence@localhost:/Users/Laruence/opensource/trunk/]
$ sapi/cli/php /tmp/1.php
Interface in 0.55554509162903 seconds, 5.5554509162903E-7 seconds per run
Structural in 0.59201097488403 seconds, 5.9201097488403E-7 seconds per run
Native in 0.34463691711426 seconds, 3.4463691711426E-7 seconds per runas you can see, it only win one time!!!
and after that, a more reallife like test I made, you can find it
here:https://gist.github.com/laruence/5877870run 3 times:
$ sapi/cli/php /tmp/2.php
Interface in 0.53591203689575 seconds, 5.3591203689575E-7 seconds per run
Structural in 0.58459782600403 seconds, 5.8459782600403E-7 seconds per run
Native in 0.34605598449707 seconds, 3.4605598449707E-7 seconds per run
[Laruence@localhost:/Users/Laruence/opensource/trunk/]
$ sapi/cli/php /tmp/2.php
Interface in 0.55550718307495 seconds, 5.5550718307495E-7 seconds per run
Structural in 0.59183287620544 seconds, 5.9183287620544E-7 seconds per run
Native in 0.35443592071533 seconds, 3.5443592071533E-7 seconds per run
[Laruence@localhost:/Users/Laruence/opensource/trunk/]
$ sapi/cli/php /tmp/2.php
Interface in 0.5577380657196 seconds, 5.577380657196E-7 seconds per run
Structural in 0.56625699996948 seconds, 5.6625699996948E-7 seconds per run
Native in 0.36081981658936 seconds, 3.6081981658936E-7 seconds per runit never won once! and there will be lots' of classes in reallife
applications, you cache hashtable will resize... it will make thing
worse..and at last, I clarified again, it's not the main reason for me to
agianst it....previous test script only measure the last all, although that,
interface already won with complex arguments signatures...here is a fixed one: https://gist.github.com/laruence/5877928
run 3 times:
$ sapi/cli/php /tmp/2.php
Interface in 1.7314801216125 seconds, 1.7314801216125E-6 seconds per run
Structural in 1.7587349414825 seconds, 1.7587349414825E-6 seconds per run
Native in 1.0431759357452 seconds, 1.0431759357452E-6 seconds per run
[Laruence@localhost:/Users/Laruence/opensource/trunk/]
$ sapi/cli/php /tmp/2.php
Interface in 1.7132070064545 seconds, 1.7132070064545E-6 seconds per run
Structural in 1.7542362213135 seconds, 1.7542362213135E-6 seconds per run
Native in 1.0379688739777 seconds, 1.0379688739777E-6 seconds per run
[Laruence@localhost:/Users/Laruence/opensource/trunk/]
$ sapi/cli/php /tmp/2.php
Interface in 1.6947190761566 seconds, 1.6947190761566E-6 seconds per run
Structural in 1.7611300945282 seconds, 1.7611300945282E-6 seconds per run
Native in 1.04856300354 seconds, 1.04856300354E-6 seconds per runas you can see, still, your patch won zero ...
okey, you said I was run in debug-mode, here is my new configure:
$ cat ./config.nice
#! /bin/sh
Created by configure
'./configure'
'--enable-opcache'
"$@"
and run 3 times:
$ sapi/cli/php /tmp/2.php
Interface in 0.77343797683716 seconds, 7.7343797683716E-7 seconds per run
Structural in 0.79890298843384 seconds, 7.9890298843384E-7 seconds per run
Native in 0.46520400047302 seconds, 4.6520400047302E-7 seconds per run
[Laruence@localhost:/Users/Laruence/opensource/trunk/]
$ sapi/cli/php /tmp/2.php
Interface in 0.90446901321411 seconds, 9.0446901321411E-7 seconds per run
Structural in 0.95593690872192 seconds, 9.5593690872192E-7 seconds per run
Native in 0.57916212081909 seconds, 5.7916212081909E-7 seconds per run
[Laruence@localhost:/Users/Laruence/opensource/trunk/]
$ sapi/cli/php /tmp/2.php
Interface in 0.73255109786987 seconds, 7.3255109786987E-7 seconds per run
Structural in 0.79073309898376 seconds, 7.9073309898376E-7 seconds per run
Native in 0.46475696563721 seconds, 4.6475696563721E-7 seconds per run
I believe, the more classes there are, the slower your patch will be.
thanks
thanks
thanks
Anthony
--
Laruence Xinchen Hui
http://www.laruence.com/--
Laruence Xinchen Hui
http://www.laruence.com/
--
Laruence Xinchen Hui
http://www.laruence.com/
Laruence,
previous test script only measure the last all, although that,
interface already won with complex arguments signatures...
here is a fixed one: https://gist.github.com/laruence/5877928
run 3 times:
$ sapi/cli/php /tmp/2.php
Interface in 1.7314801216125 seconds, 1.7314801216125E-6 seconds per run
Structural in 1.7587349414825 seconds, 1.7587349414825E-6 seconds per run
Native in 1.0431759357452 seconds, 1.0431759357452E-6 seconds per run
[Laruence@localhost:/Users/Laruence/opensource/trunk/]
$ sapi/cli/php /tmp/2.php
Interface in 1.7132070064545 seconds, 1.7132070064545E-6 seconds per run
Structural in 1.7542362213135 seconds, 1.7542362213135E-6 seconds per run
Native in 1.0379688739777 seconds, 1.0379688739777E-6 seconds per run
[Laruence@localhost:/Users/Laruence/opensource/trunk/]
$ sapi/cli/php /tmp/2.php
Interface in 1.6947190761566 seconds, 1.6947190761566E-6 seconds per run
Structural in 1.7611300945282 seconds, 1.7611300945282E-6 seconds per run
Native in 1.04856300354 seconds, 1.04856300354E-6 seconds per runas you can see, still, your patch won zero ...
Your benchmark is timing way too much. It needs to narrow the measurement
surface significantly:
https://gist.github.com/ircmaxell/5878060
That measures only the dispatch time (discounting differences in object
construction, etc). And when I run that:
$ sapi/cli/php laruence_test.php
Interface in 0.51537394523621 seconds, 1.7179131507874E-7 seconds per run
Structural in 0.51391339302063 seconds, 1.7130446434021E-7 seconds per run
Native in 0.31268692016602 seconds, 1.0422897338867E-7 seconds per run
$ sapi/cli/php laruence_test.php
Interface in 0.52541399002075 seconds, 1.7513799667358E-7 seconds per run
Structural in 0.54076099395752 seconds, 1.8025366465251E-7 seconds per run
Native in 0.32039928436279 seconds, 1.0679976145426E-7 seconds per run
$ sapi/cli/php laruence_test.php
Interface in 0.51772499084473 seconds, 1.7257499694824E-7 seconds per run
Structural in 0.50954604148865 seconds, 1.6984868049622E-7 seconds per run
Native in 0.31669473648071 seconds, 1.0556491216024E-7 seconds per run
$ sapi/cli/php laruence_test.php
Interface in 0.51783013343811 seconds, 1.7261004447937E-7 seconds per run
Structural in 0.50820827484131 seconds, 1.6940275828044E-7 seconds per run
Native in 0.3112211227417 seconds, 1.0374037424723E-7 seconds per run
$ sapi/cli/php -v
PHP 5.6.0-dev (cli) (built: Jun 27 2013 12:35:30)
Copyright (c) 1997-2013 The PHP Group
Zend Engine v2.6.0-dev, Copyright (c) 1998-2013 Zend Technologies
(a non-debug build, no extensions)
In this case, which also ignores the compile time of interface resolving
(which does add slowdown to the compile step, which a fair benchmark would
take into count).
But these runs show something interesting. Interfaces average
mathematically to 1.730283E-7 per call. Structural hints average
to 1.7270175E-7
Both are within the margin of error of the tests to prove that at worst
they are the same speed.
Which shows that unless you want to argue about literally microseconds, I
think we can put this performance piece to rest...
Especially that "runtime is slow" which has been brought up over and over
again has been disproven... The worst case that you could make a
justification for is ignoring 1/2 the interface's cost... Which is pretty
silly in a holistic discussion...
So can we please get past this "performance" issue, and talk about
something that actually matters...?
wow... why did this just become a measuring contest. Do we need to go
recruit people interested and attach their resume's?
I'll just toss in I am interested in this. Comparing to current methods of
doing this sort of things(because if you're denying it exists in the wild,
lets open up some of your code and be honest) I think the error is more
useful then throwing an exception of causing a fatal error.
- The error clearly specifies what is missing. It also leads to
documentation in the form of the structured interface where you can
understand why it failed and what the expectations are. - The error happens at the start of the call not in embedded in the
code, possibly hidden from common runtime by conditions. This seems like a
more ideal fail early sort of situation.
I also think this is a cool thing we'll see someday useful in project
integration. Say I'm building on top of a framework or library and want to
use an implementation but want to treat it more like an interface and
provide additional implementations. Not a stretch. My options are, don't
type, hack this 3rd party tool, wait for another release where they might
have addressed my concern. Instead I could provide this sort of interface
and provide additional implementations of the interface and call it a day.
If someday they add an interface its not a big change to take advantage of
it and its forwards compatible if the interface matches the structured
interface.
So real work example out of the way, can we look at technical concerns?
On Thu, Jun 27, 2013 at 10:35 PM, Anthony Ferrara ircmaxell@gmail.com
wrote:Stas et al,
So, the question of what is the difference between the two errors
remains unanswered. If the whole diff is that one of the errors has
word
"recoverable" in the message, it's not substantial difference at all
and
one that does not require new syntax and big change in the language.I'm assuming that you do know the difference between
E_RECOVERABLE_ERROR
and E_ERROR. And the difference is not trivial...Internals should not be taking sides on what's good practice and
what's
bad practice (if it was, why the heck was goto introduced?).
Instead, itCan we please lay the goto argument to rest? The argument "goto was
introduced, so feature X must too, because goto was introduced" didn't
sound good even the first time...I'm absolutely not trying to say we should include this because GOTO was
introduced. I'm trying to point out the circular nature of the argument
of
good-vs-bad practice in general...should enable today's good practice to be followed. But it should not
take a stand about bad practice.In my opinion, we should. We should not take into the language any
concept that anyone considers useful in some particular piece of code.
PHP language is very widely used, and we should consider how it would
influence this huge ecosystem and if the overall effect would be
beneficial and justify complicating (if there is one) the whole
language
system.Language is a system of thought and a system of approaching
communication. IMO, this means it should have some principles and not
just be a random bag of things that somebody at one point or another
decided to stick into it, they should make sense together. PHP doesn't
have the best reputation in this regard, but we are trying to make it
better, not worse.It does not mean we should avoid change. It means we should have good
reasons for change and carefully consider it. Good use cases IMO are
prerequisite for that.Of course they are. Use-cases matter. A lot.
But "good practice" vs "not-good-practice" shouldn't be a significant
factor, because your "good-practice" is different from mine. And unless
we
as a group decide to stick to one interpretation (we haven't), then it's
kind of pointless to talk about good practice. If you want to vote
based on
it, that's your prerogative. But in general I think that thought
process is
dangerous for the community as a whole and for core...My point here is that we should be judging features by
their merit alone, and not by how we would use them. We also should
not
be judging them based upon our preferred style, but on the overall
case
of what it aims to achieve.IMO there's no merit in the feature besides its use. That's the only
merit a feature could or should ever have.Ok, so then you agree that "best-practice" doesn't come into it at
all...?Bringing this back on point, Duck-typing is a very valid and
accepted
way of doing OOP. In fact most other dynamic languages use this as
the
basis for their OOP system. This proposal does nothing but attempt toIn fact, most other dynamic languages don't even have parameter typing.
Neither Perl, Python, Ruby or Javascript have it. Let alone typing of
the kind you suggest. What they have we have too. Duck typing for them
doesn't mean what you propose - it means what we already have, checking
type at the point of use. Check
https://en.wikipedia.org/wiki/Duck_typing and see the examples - most
of
them don't have any typechecks.
Referring to "most dynamic languages" while promoting this proposal is
a
bit misleading.Those other languages (all of them in fact) throw exceptions if the
function or method does not exist. PHP hard fatals. They can live with
pure
duck-typing because their engines are designed to stay running, where
ours
is designed to fall on its face. This proposal is one attempt to bring
some
consistency and recoverability to the dynamic aspect of programming
while
providing for the ability to verify APIs at the engine level.One thing I find interesting is that I have discussed this feature with
about 50 people at this point (quite a few at conferences and such
before
actually proposing it), and the sentiment elsewhere (not on list) was
very
predominately "this solves a ton of problems". I find it interesting
that
on-list people seem to think that I'm making the use-cases up, and that
there's not really a problem to solve. I wish some of the other people I
talked to would speak up here ;-)...One thing to note is who the major audience for a feature like this is.
It's not predominately for 1st party developers (developers writing
one-off
applications). It's not predominately for 2nd party developers
(developers
writing frameworks and other applications that have few dependencies and
are intended to be used by 1st party developers). It is primarily for
3rd
party developers. These are developers that maintain code bases built on
top of 2nd party code, but meant to be used by 1st party developers.What does that mean? The average one-off application builder is likely
to
not get much use out of this (it can be used, but the problems for this
group of people are mostly solved already). The average framework
core-dev
(like Symfony or ZF) isn't going to get much benefit out of this
either
(it can help in some areas, and they will use it, but it's not solving
major problems). The Drupals, Magentos and Fuel CMS's of the world. The
people who make plugins and modules for Frameworks and CMS's.Basically, the people who's lives will get MUCH easier with something
like
this are the people who maintain code that has a lot of dependencies,
and
that other people depend on (it sits in the middle of the dependency
chain,
where code on both sides is outside of their control).So if you don't see the usefulness of this type of change, ask if you
maintain anything significant that fits that description. If you don't,
find someone who does and talk to them. Otherwise you may be unfairly
judging by ignoring problems that you personally don't have...
what's going on here?Stas has been a core contributor for a long time, if he didn't meet
such usage, then I think 80%(or more) people doesn't have this need
either.to be honest, I don't think this is useful either... (in case you
will doubt my qualification, let me introduce myself a little more, I
am a 6 years PHP, 5 year C engineer, develped a lots of PHP
applications, and also some big scalar ones, like weibo.com which I am
maintaining now, thanks)interface is enough, interface is exactlly doing the protocol control
thing, and I think it does very well. and it also very strict and
reliable.so, why we need such feature that only a few people need it( if there
really is some).I missed one thing here, it should be:
why we need such feature that only a few people need it, and will
also make reset people confused, and most of other language doesn't
have it, and using it will make things a little mess(and low
performance vs pure interface)?thanks
and about the performance, runtime check on every methods's signature
in runtime, how could it be faster than current way?if you bring 1% slowdown in one feature, yes, it's trivail, but if
you bring such features 100 times?and, I have to clarifiy, the main reason I don't like this proposal is
usage, not the performance..and last, I am poor at english, so I usually don't like to argue.
sorry if I didn't make myself clear.thanks
Anthony
PS: I updated the RFC with a number of use-cases, including the ability
to
hint on Traits (more specifically, the API of a trait)...--
Laruence Xinchen Hui
http://www.laruence.com/--
Laruence Xinchen Hui
http://www.laruence.com/
...
group of people are mostly solved already). The average framework core-dev
(like Symfony or ZF) isn't going to get much benefit out of this either
(it can help in some areas, and they will use it, but it's not solving
major problems). The Drupals, Magentos and Fuel CMS's of the world. The
people who make plugins and modules for Frameworks and CMS's.
That's not exactly true. As a core developer of a major framework (zf),
I see a metric ton of value in this. Most of the value comes in the
form of how we communicate upstream and downstream dependencies,
requirements, and usage.
Right now, there is a rock and a hard place - and not much else. The
community wants consistent APIs for tools that solve cross cutting
problems (cache, logging, etc). But the only solution is to
collectively, as a community, pick a name, a governing body, and
physical repository. The manifestation of this is the PSR/Fig group.
At current, people who wish to participate have to
a) aid in the decision processs,
b) help craft the code, and host it as part of the 3rd party, then
c) depend (phyically) on the code/interfaces that comes out of a+b
(C) is a tough problem b/c not everyone wants to use a package
management system to pull in these now required 3rd party dependencies,
or alternatively ship them as part of our own code base.
What would be ideal is if we can all generally agree on an API and
ship our own interfaces which are structurally equivalent, thus reducing
all external dependencies. This, from a communication standpoint, makes
consumption easier between projects.
Physically,
Zend\Logger\LoggerInterface (zf base log interface)
Symfony\Mvc\LogInterface (sy)
Monolog\LoggerInterface
.. more ..
would all be structurally equivalent through their participation in
FIG/PSR3, and instances of an implementation of any of them could
structually pass for an instance of another.
This is better than what we have now which is having a dependency on
some 3rd party's Logger interface: (PSR\PSR3\LoggerInterface).
So if you don't see the usefulness of this type of change, ask if you
maintain anything significant that fits that description. If you don't,
find someone who does and talk to them. Otherwise you may be unfairly
judging by ignoring problems that you personally don't have...
Here's the important part for people that don't see the use case:
There's no doubt that large groups of developers in the PHP ecosystem
are sharing code more now than I've seen in my 15 years in it. It would
be nice if the engine would facilitate or simplify this kind of
code-sharing in a way that our instanceof/is_a history and roots simply
can't.
-ralph
On Thu, Jun 27, 2013 at 6:38 PM, Ralph Schindler
ralph@ralphschindler.com wrote:
...
group of people are mostly solved already). The average framework core-dev
(like Symfony or ZF) isn't going to get much benefit out of this either
(it can help in some areas, and they will use it, but it's not solving
major problems). The Drupals, Magentos and Fuel CMS's of the world. The
people who make plugins and modules for Frameworks and CMS's.That's not exactly true. As a core developer of a major framework (zf), I
see a metric ton of value in this. Most of the value comes in the form of
how we communicate upstream and downstream dependencies, requirements, and
usage.Right now, there is a rock and a hard place - and not much else. The
community wants consistent APIs for tools that solve cross cutting problems
(cache, logging, etc). But the only solution is to collectively, as a
community, pick a name, a governing body, and physical repository. The
manifestation of this is the PSR/Fig group.At current, people who wish to participate have to
a) aid in the decision processs,
b) help craft the code, and host it as part of the 3rd party, then
c) depend (phyically) on the code/interfaces that comes out of a+b(C) is a tough problem b/c not everyone wants to use a package management
system to pull in these now required 3rd party dependencies, or
alternatively ship them as part of our own code base.What would be ideal is if we can all generally agree on an API and ship
our own interfaces which are structurally equivalent, thus reducing all
external dependencies. This, from a communication standpoint, makes
consumption easier between projects.Physically,
Zend\Logger\LoggerInterface (zf base log interface)
Symfony\Mvc\LogInterface (sy)
Monolog\LoggerInterface
.. more ..would all be structurally equivalent through their participation in
FIG/PSR3, and instances of an implementation of any of them could
structually pass for an instance of another.This is better than what we have now which is having a dependency on some
3rd party's Logger interface: (PSR\PSR3\LoggerInterface).So if you don't see the usefulness of this type of change, ask if you
maintain anything significant that fits that description. If you don't,
find someone who does and talk to them. Otherwise you may be unfairly
judging by ignoring problems that you personally don't have...Here's the important part for people that don't see the use case:
There's no doubt that large groups of developers in the PHP ecosystem are
sharing code more now than I've seen in my 15 years in it. It would be nice
if the engine would facilitate or simplify this kind of code-sharing in a
way that our instanceof/is_a history and roots simply can't.-ralph
--
Hi,
Could you give some real scenarios for this since you are in such a
position and you also see this metric ton of goodness? I think they
would help understanding the benefits much better and would also help
clear the waters a but.
Can you come up with some bad scenarios for this as well?
Best regards
Florin Patan
https://github.com/dlsniper
http://www.linkedin.com/in/florinpatan
Hi!
I'm assuming that you do know the difference between
E_RECOVERABLE_ERROR
and E_ERROR. And the difference is not trivial...
Could you please explain what is the non-trivial difference relevant to
this case and how it is relevant? Could you also explain why making
undefined function produceE_RECOVERABLE_ERROR
would not solve it?
But "good practice" vs "not-good-practice" shouldn't be a significant
factor, because your "good-practice" is different from
In my opinion, it is the most significant factor. Good practices are
very important to the language, because they are what turns the language
into a community with common... well, language of expressing things.
Otherwise it is just an assembly of loosely related hacks.
Those other languages (all of them in fact) throw exceptions if the
function or method does not exist. PHP hard fatals. They
Here it goes again - the exception thing. If you want to make PHP throw
exceptions on errors, please write RFC on making PHP throw exceptions on
errors (I have no idea how it would help you, but you are welcome to
share it). Writing a different parallel typing system into PHP and
linking it to duck typing in other dynamic languages which explicitly
doesn't do what you claim it does and what your proposal does in not
right way to approach it. Other dynamic languages - excepting Scala and
couple of others, which are very unlike PHP in a lot of aspects
including static typing - demonstrably do not do what you want to do. So
if you say "look at other languages" - yes, we did and they call "duck
typing" exactly what I described, not what you described.
So if you don't see the usefulness of this type of change, ask if you
maintain anything significant that fits that description. If you
don't, find someone who does and talk to them. Otherwise you may be
unfairly judging by ignoring problems that you personally don't have...
Thank you for your advice, in my 20+ year career in software development
and 10+ year career in PHP specifically I have has some experience with
all kinds of problems. Now if you would like to kindly move from "you
know nothing, Jon Snow" to more substantial arguments it'd be more
productive.
--
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227
Stas,
On Thu, Jun 27, 2013 at 1:18 PM, Stas Malyshev smalyshev@sugarcrm.comwrote:
Hi!
I'm assuming that you do know the difference between
E_RECOVERABLE_ERROR
and E_ERROR. And the difference is not trivial...
Could you please explain what is the non-trivial difference relevant to
this case and how it is relevant? Could you also explain why making
undefined function produceE_RECOVERABLE_ERROR
would not solve it?
Well, changing undefined functions to E_RECOVERABLE_ERROR
would make it
easier to safely recover.
But it still doesn't solve the coupling problem between multiple libraries.
To quote Ralph here:
There's no doubt that large groups of developers in the PHP ecosystem are
sharing code more now than I've seen in my 15 years in it. It would be
nice if the engine would facilitate or simplify this kind of code-sharing
in a way that our instanceof/is_a history and roots simply can't.
changing undefined method to E_RECOVERABLE_ERROR
(while likely good),
doesn't do anything for that problem...
But "good practice" vs "not-good-practice" shouldn't be a significant
factor, because your "good-practice" is different from
In my opinion, it is the most significant factor. Good practices are
very important to the language, because they are what turns the language
into a community with common... well, language of expressing things.
Otherwise it is just an assembly of loosely related hacks.
If we were talking about consistent good practice, such as those that were
codified and agreed upon by the community as a whole, or as those
determined by a BDFL, I could completely agree. Without either of those,
it's just a biased and loosely assembled collection of
"whatever-people-who-have-political-influence-want". Which is not what
makes a good language or community...
Those other languages (all of them in fact) throw exceptions if the
function or method does not exist. PHP hard fatals. They
Here it goes again - the exception thing. If you want to make PHP throw
exceptions on errors, please write RFC on making PHP throw exceptions on
errors (I have no idea how it would help you, but you are welcome to
share it). Writing a different parallel typing system into PHP and
linking it to duck typing in other dynamic languages which explicitly
doesn't do what you claim it does and what your proposal does in not
right way to approach it. Other dynamic languages - excepting Scala and
couple of others, which are very unlike PHP in a lot of aspects
including static typing - demonstrably do not do what you want to do. So
if you say "look at other languages" - yes, we did and they call "duck
typing" exactly what I described, not what you described.
No, my point was that you pulled the card "those languages don't have these
structural typing thigns". And I showed one reason they don't.
This argument is getting circular really fast...
So if you don't see the usefulness of this type of change, ask if you
maintain anything significant that fits that description. If you
don't, find someone who does and talk to them. Otherwise you may be
unfairly judging by ignoring problems that you personally don't have...
Thank you for your advice, in my 20+ year career in software development
and 10+ year career in PHP specifically I have has some experience with
all kinds of problems. Now if you would like to kindly move from "you
know nothing, Jon Snow" to more substantial arguments it'd be more
productive.
I think you very much misunderstood what that paragraph was supposed to
mean. I don't pretend to know everything. I don't pretend to know every
use-case. And I don't pretend to know the plights of every developer. All I
know is what I experience and the experiences of those that I talk to. I
have seen the usefulness here. And I know others have (they
have expressed it here and in other channels).
And I know you have had difference experiences. And if you don't see the
usefulness, I was suggesting that perhaps it may be prudent to look
towards those (other than myself) who have seen the problem. Someone like
Ralph from this very thread.
It's something that community leaders like us have to do. Branch out and
realize that our own perceptions and viewpoints are going to
be naturally biased by our experiences. And that's why I suggested reaching
out to other community maintainers for some insight into what problems they
face.
It was most definitely not a "you know nothing" argument, and I hope that
you know that.
Anthony
Hi!
No, my point was that you pulled the card "those languages don't have
these structural typing thigns". And I showed one reason they don't.
Sorry, it was you who pulled the card of "other dynamic languages have
duck typing". I have showed that what other dynamic languages have and
what they call duck typing, PHP has too, and has had since forever. What
you call duck typing is completely different and no non-statically-typed
language, as far as I can see, has it at all. You can argue PHP should
be a first dynamic typing language in the world to feature this kind of
type checking, but when you say "In fact most other dynamic languages
use this as the basis for their OOP system" - you can not use this to
support your proposal, because what they are doing is very different
from what you are doing, and in fact none of them are doing what you are
doing at all.
All I know is what I experience and the experiences of those that I talk
to. I have seen the usefulness here. And I know others have (they
have expressed it here and in other channels).
The only thing left is to move from "I see the usefuless" to "I can show
the usefulness to others in a convincing way".
It's something that community leaders like us have to do. Branch out and
realize that our own perceptions and viewpoints are going to
be naturally biased by our experiences. And that's why
I suggested reaching out to other community maintainers for some insight
into what problems they face.
Great. Please do reach out and provide some insight of what problems
require such approach and why. So far the best argument I have seen for
this proposal is that it allows to convert E_ERROR
to
E_RECOVERABLE_ERROR. I don't think it's nearly enough for introducing a
new and unprecedented typing system into the language.
--
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227
I agree the use-cases are slightly weak. This is a draft RFC. It's supposed to help identify
the areas where we can improve it. Help identify use-cases. Help dig it out.
I think Ralph has the right idea:
register_compatible_types($yourType, $myType);
A better name might be (only for interfaces):
register_instanceof($compatibleInterface, $interface);
e.g.
class ThirdParty {
static foo(LoggerInterface $log) {}
}
class Bar implements My_Logging_Inferface {}
echo (Bar instanceof LoggerInterface); // false
register_instanceof('My_Logging_Inferface', 'LoggerInterface');
echo (Bar instanceof LoggerInterface); // true
ThirdParty::foo(new Bar); // no error since 'My_Logging_Interface' is compatible with LoggerInterface
In summary, it would allow for user-land registration of compatible interfaces to third party libraries.
There's definitely a couple of use cases where I would have liked to use something to this.
Advantages to wrapping the feature in a function:
- No extra syntax to support in core
- No 'new concept' for developers to learn
- It can be disabled (which OO purists might like)
disable_functions = register_instanceof
Hi Anthony,
I really like the idea. But I'd prefer the following syntax:
public function __construct(~HttpKernelInterfaceForClientIp $parent) {
$this->parent = $parent;
}
Reasons:
- A passed object does not have to explicitly implement the interface, but
only implicitly ("practically", "more or less"). The tilde conveys that
semantic very well. - As you mentioned, brackets are (IMO too) close to generics.
Cheers,
Bernhard
--
Bernhard Schussek
Blog: http://webmozarts.com
Twitter: http://twitter.com/webmozart
2013/6/25 Anthony Ferrara ircmaxell@gmail.com
Hey all,
I want to throw out this draft RFC to get the concept floating around and
get feedback early.https://wiki.php.net/rfc/protocol_type_hinting
There are still open questions, and a more complete (and cleaner)
implementation will be needed before officially proposing it, but I wanted
to get the concept out as soon as possible.What do you think?
Thanks!
Anthony
Hey Anthony,
thanks for your work and I think it’s a very good idea. I thought very much of it during the last days and I start to see its merits. So, good thing. I would prefer ~Type for syntax over <Type> though, but that's just a minor detail.
cu,
Lars
Am 25.06.2013 um 17:57 schrieb Anthony Ferrara ircmaxell@gmail.com:
Hey all,
I want to throw out this draft RFC to get the concept floating around and
get feedback early.https://wiki.php.net/rfc/protocol_type_hinting
There are still open questions, and a more complete (and cleaner)
implementation will be needed before officially proposing it, but I wanted
to get the concept out as soon as possible.What do you think?
Thanks!
Anthony