# RFC: Protocol Type Hinting

- Thread: https://externals.io/message/67809
- Original archive: https://news-web.php.net/php.internals/67809
- Started by Anthony Ferrara on 2013-06-25T15:57:15+00:00
- Messages: 50
- RFC: Structural Type Hinting (Withdrawn) — https://externals.io/rfc/protocol_type_hinting.md
- Page 1 of 2: messages 1 to 25 (25 per page, add `?page=N` to the URL)
- Next page: https://externals.io/message/67809.md?page=2
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · RFCs at https://externals.io/rfc.md · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #67809 — Anthony Ferrara — 2013-06-25T15:57:15+00:00

URL: https://externals.io/message/67809#67809

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

---

## #67811 — Nikita Nefedov — 2013-06-25T16:15:01+00:00

URL: https://externals.io/message/67809#67811 · In reply to: #67809

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 :)

---

## #67812 — Anthony Ferrara — 2013-06-25T16:24:01+00:00

URL: https://externals.io/message/67809#67812 · In reply to: #67811

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

---

## #67814 — Laruence — 2013-06-25T16:32:20+00:00

URL: https://externals.io/message/67809#67814 · In reply to: #67809

> 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/

---

## #67815 — Michael Wallner — 2013-06-25T16:39:33+00:00

URL: https://externals.io/message/67809#67815 · In reply to: #67814

> 
> > […]
>
> 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

---

## #67818 — Ralph Schindler — 2013-06-25T18:47:59+00:00

URL: https://externals.io/message/67809#67818 · In reply to: #67809

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

---

## #67822 — Yahav Gindi Bar — 2013-06-25T19:25:41+00:00

URL: https://externals.io/message/67809#67822 · In reply to: #67818

>
> > […]
>
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?

---

## #67827 — Stas Malyshev — 2013-06-25T20:16:28+00:00

URL: https://externals.io/message/67809#67827 · In reply to: #67809

Hi!

> 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

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

---

## #67831 — Anthony Ferrara — 2013-06-25T21:54:02+00:00

URL: https://externals.io/message/67809#67831 · In reply to: #67814

Laruence,

Hey:
>    Just one question, usage?  why we need this? (run-time check is
> slow and I can not think out of a case then we must need it)
>

In practice, the run-time check shouldn't be that slow. In fact, I just did
a quick micro-benchark without actually implementing caching, and it's only
8% slower than an interface check. To put that in perspective, it adds on
average .387e-8 seconds per function call.

And that's without caching.

With caching, it should be no slower than a standard interface. In fact, it
could potentially be *faster* than an interface. The primary reason is that
the interface check happens at class definition time regardless of if it's
used or not. A cached protocol check would only be run when it's actually
hinted against.

So this does have the potential to actually increase the performance of a
codebase overall. So no, "run-time checks" are not really slow (they are no
more expensive than compile-time checks) as long as we can cache them
(still working on an effective cache now)...

As a proof-of-concept, I implemented a primitive cache. Here's the results
comparing an Interface check to a Protocol check and Native:

Interface in 0.50202393531799 seconds, 5.0202393531799E-7 seconds per run
Protocol in 0.48089909553528 seconds, 4.8089909553528E-7 seconds per run
Native in 0.3850359916687 seconds, 3.850359916687E-7 seconds per run

Now, Interface and protocol area always within about 2% of each other, and
which is faster changes per run, so I think it's safe to say this method is
not slow...


>    Go does that is not a reason...
>

 I never said it was. I quoted it to show one inspiration for the idea.

As far as what the use-case is, there are a few:

1. Library implementers don't need to declare a dependency for a third
party library to use the interface from it.

This may sound trivial, but imagine this. Right now Zend and Symfony have
very similar providers for certain tasks. One of the ones that comes to
mind (besides logging) is caching. If you want to use a Zend component in
an Symfony app while still using caching today, you'd need to shim together
an adapter to satisfy the zend cache interface with the symfony cache
interface. Which means your project now depends on all three, as well as
requiring the zend cache code for the sole purpose of loading the interface
required by the component.

That's loading a LOT of code that you don't need, just to avoid having to
setup two caches...

Instead, the zend component could depend on the narrow api that it needs.
It only calls the ->get($key) and ->set($key, $value) methods, so create a
(new?) interface with that limited API, and then type against it as a
protocol. Now that interface still would need to be loaded, but the Symfony
class can resolve that interface directly. Without the need to load
anything from Zend cache (said caching interface doesn't need to be there)
or wire anything else for you.

This may seem like a triviality, but it's actually quite significant.
Because...

2. It's dependency inversion applied to the type system.

Think of it like this. Dependency Injection is dependency inversion, it
pushes the requirement to resolve a dependency onto the creator instead of
on the object itself.

Protocols are also dependency inversion, it pushes the requirement to
determine if a dependency is resolve onto the receiver instead of the
creator of the object.

It is *not* a replacement for interfaces. It's not intended to be. Instead,
it's useful along side for cases when you want interoperability and
duck-style-typing without the overhead of coupling the packages together
via interfaces.

3. It allows avoiding interface-hell

Right now, every time you want to create a polymorphic dependency, you need
to create an interface for it to allow for others to resolve that with a
class from a different tree. This can lead to situations where you have
literally dozens of interfaces which you may or may not need. Additionally,
you're put in a position by classes like PDO where you can't have a
polymorphic dependency because there's no interface defined.

This change allows for using a Class as the protocol, so you could
type-hint against the API of a class without needing to create a separate
interface for it. This wouldn't be useful for something you substitute
often, but would be VERY useful for libraries which wrap dependencies that
aren't changed often, but may need to be. So instead of creating a PDO
wrapper just to add a series of interfaces for it, just hint against <PDO>.
That way you're getting an object that looks like PDO, but doesn't need to
be PDO if you don't want it to be...

There are definitely more, but the first one I think is the big one. It
decouples while still providing interface safety...



Anthony


PS: here's the code for said benchmark:

interface Foo {
        public function foo();
}

class Bar {
        public function foo() {}
}

class Baz implements Foo {
        public function foo() {}
}

function benchmark($func, $times, $arg) {
        $s = microtime(true);
        for ($i = 0; $i < $times; $i++) {
                $func($arg);
        }
        $e = microtime(true);
        return $e - $s;
}
$times = 1000000;
$interface = benchmark(function(Foo $foo) {}, $times, new Baz);
echo "Interface in $interface seconds, " . ($interface / $times) . "
seconds per run\n";
$protocol = benchmark(function(<Foo> $foo) {}, $times, new Bar);
echo "Protocol in $protocol seconds, " . ($protocol / $times) . " seconds
per run\n";
$native = benchmark(function($foo) {}, $times, new Bar);
echo "Native in $native seconds, " . ($native / $times) . " seconds per
run\n";

---

## #67832 — Anthony Ferrara — 2013-06-25T22:09:34+00:00

URL: https://externals.io/message/67809#67832 · In reply to: #67827

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:

1. 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.

2. 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

---

## #67833 — Stas Malyshev — 2013-06-25T22:16:34+00:00

URL: https://externals.io/message/67809#67833 · In reply to: #67831

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

---

## #67834 — Yahav Gindi Bar — 2013-06-25T22:21:51+00:00

URL: https://externals.io/message/67809#67834 · In reply to: #67833

On Wed, Jun 26, 2013 at 1:16 AM, Stas Malyshev <smalyshev@sugarcrm.com>wrote:

> Hi!
>
> > […]
> together
> > […]
> interface
> > […]
>
> 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."..



> > […]
> 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?
>
> > […]
>
> 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

---

## #67835 — Stas Malyshev — 2013-06-25T22:41:23+00:00

URL: https://externals.io/message/67809#67835 · In reply to: #67832

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?

> 1. 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...

> 2. 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

---

## #67836 — Anthony Ferrara — 2013-06-25T22:43:01+00:00

URL: https://externals.io/message/67809#67836 · In reply to: #67833

Stas,


On Tue, Jun 25, 2013 at 6:16 PM, Stas Malyshev <smalyshev@sugarcrm.com>wrote:

> Hi!
>
> > […]
> together
> > […]
> interface
> > […]
>
> 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.
> > […]
> 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?


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...


> > […]
>
> 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"...

---

## #67838 — Florin Patan — 2013-06-25T22:54:03+00:00

URL: https://externals.io/message/67809#67838 · In reply to: #67831

Hi,


> Laruence,
>
> Hey:
> > […]
>

> 1. Library implementers don't need to declare a dependency for a third
> party library to use the interface from it.
>
> This may sound trivial, but imagine this. Right now Zend and Symfony have
> very similar providers for certain tasks. One of the ones that comes to
> mind (besides logging) is caching. If you want to use a Zend component in
> an Symfony app while still using caching today, you'd need to shim together
> an adapter to satisfy the zend cache interface with the symfony cache
> interface. Which means your project now depends on all three, as well as
> requiring the zend cache code for the sole purpose of loading the interface
> required by the component.
>
> That's loading a LOT of code that you don't need, just to avoid having to
> setup two caches...
>
> Instead, the zend component could depend on the narrow api that it needs.
> It only calls the ->get($key) and ->set($key, $value) methods, so create a
> (new?) interface with that limited API, and then type against it as a
> protocol. Now that interface still would need to be loaded, but the Symfony
> class can resolve that interface directly. Without the need to load
> anything from Zend cache (said caching interface doesn't need to be there)
> or wire anything else for you.
>


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

---

## #67839 — Stas Malyshev — 2013-06-25T22:57:46+00:00

URL: https://externals.io/message/67809#67839 · In reply to: #67836

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

---

## #67840 — Anthony Ferrara — 2013-06-25T22:59:29+00:00

URL: https://externals.io/message/67809#67840 · In reply to: #67835

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...


> > […]
>
> 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...


> > […]
>
> 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...


> > […]
>
> 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

---

## #67841 — Stas Malyshev — 2013-06-25T23:16:21+00:00

URL: https://externals.io/message/67809#67841 · In reply to: #67840

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

---

## #67842 — Robert Stoll — 2013-06-25T23:28:01+00:00

URL: https://externals.io/message/67809#67842 · In reply to: #67841

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

---

## #67849 — Nikita Nefedov — 2013-06-26T09:51:13+00:00

URL: https://externals.io/message/67809#67849 · In reply to: #67839

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

---

## #67865 — Seva Lapsha — 2013-06-26T13:27:44+00:00

URL: https://externals.io/message/67809#67865 · In reply to: #67849

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
> > […]
>
> 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

---

## #67872 — Anthony Ferrara — 2013-06-26T14:28:31+00:00

URL: https://externals.io/message/67809#67872 · In reply to: #67841

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.


> > […]
>
> 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
> > […]
>
> 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...

---

## #67876 — Florin Patan — 2013-06-26T14:46:38+00:00

URL: https://externals.io/message/67809#67876 · In reply to: #67872

> Stas and all,
>
>
> But instead a fatal "protocol does not match" error will be thrown. The
> > […]
>
>
> 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.
>
>
> > […]
>
>
> 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...
>
> > […]
>
>
> 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

---

## #67878 — Robert Stoll — 2013-06-26T14:52:47+00:00

URL: https://externals.io/message/67809#67878 · In reply to: #67872

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.


> > […]
>
> 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
> > […]
>
> 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...

---

## #67880 — Anthony Ferrara — 2013-06-26T15:02:38+00:00

URL: https://externals.io/message/67809#67880 · In reply to: #67876

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

---

Next page: https://externals.io/message/67809.md?page=2
