Hello all,

I purposely tried to stay out of this conversation, but seeing as
there's a lot of information flying around I think I'll give my
$0.02...

As far as the Square-Rectangle example, that is a classic violation of
the LSP.  In fact, it's unsolvable using inheritance.  For information
as to why, check out this article:
http://en.wikipedia.org/wiki/Circle-ellipse_problem

As far as those who say that this is not an LSP issue, it actually is.
 Look at the LSP definition again.  I'll post it here:

> Let q(x) be a property provable about objects x of type T. Then q(y) should 
> be provable for objects y of type S where S is a subtype of T.

That means that extending methods actually cannot be more general than
the parent.  More generic breaks since another subtype of the parent
class is free to not make the generalization and hence break the
substitution ability.  And more specific breaks the substitution as
well since it if you swap out a more generic method with a more
specific one, it can break everything.

Let's be clear here.  This is not about *what works*, but about what
is known to work.  To see the problem with breaking signatures, let's
look at the three cases (interface, abstract class and inheritance.

Interfaces are a contract between your implementation and the outside
world.  The method signature is the contract.  It's telling everyone
that uses it (not implements, but type hints against) that all
implementations that it receives will behave to that contract.  Sure,
we could go with duck typing and just provide the method name (no
arguments) in the interface, but that's a pretty lousy contract.  At
that point, why even bother with interfaces?  The interface defines
what should be accepted, and any method that implements it should
accept exactly that (no more, no less, no different).  Otherwise
you'll violate the contract and couple to the implementation instead
of the interface.  This causes the checked polymorphic ability of the
interface to go out the window.  The result is that the interface
becomes completely useless.  So, in order for interfaces to be useful,
they should include the exact arguments in order and in type, and the
runtime (PHP) should enforce that (which it does).

Abstract classes are a contract between your implementation and the
parent class only.  They are used when you're implementing
functionality, and you leave some methods for the child to define.
But these definitions are important since the parent is likely calling
them directly (or implementing part of an interface, in which the
abstract method becomes an interface contract).  So the parent is
expecting a specific signature, and the child is expected to implement
it.  Now, the interesting thing here, is that in non-interface
abstract class methods, generalization is ok in the child.  You can
make the child method more general (take more arguments, take broader
arguments, etc) as long as it still behaves according to the interface
provided.  This is because the contract is to a known implementation
(the parent), it's not a general contract.  But in PHP, you can't do
that since it uses the same contract enforcer for interfaces as it
does for abstract classes.

Overriding Methods are seemingly contractless, so you should be able
to change the method signature from subclass to subclass.  Indeed, on
a simple first look, this makes sense.  However, when we start to look
at it, it becomes apparent that there is actually a contract in place
due to the LSP.  We can't change the function signature, since we
expect all subtypes of T will be able to be swapped for each other.
If we could change signatures, we'd loose this ability and hence
violate LSP.  Plus, this would also make the code completely resistant
to the fundamental principle of OOP, polymorphism.  Sure, you could
swap out some of the subclasses, but not all.  So all of a sudden, by
loosening the restrictions on extended method parameters, we're
actually causing tighter coupling and all but eliminating the benefits
of OOP in general.



With respect to the func_get_args argument, I see that as a non-issue.
 Sure, you can do it.  But if you do, you're lying about the
interface.  You're telling the callers that you expect no arguments,
but then all of a sudden you error out.  You're pushing all of the
interface declaration logic out of the interface and into code.
That's only going to create maintainability, readability and quality
issues as time goes on.  Realistically, the only good use of
func_get_args is when you need to take in an unlimited number of
arguments.  But if you do that, you should include the minimum number
in the API itself.  So if you have a function add() that can take any
number of integers, the minimum that makes sense is 2.  So you should
declare add($left, $right), and then use func_get_args to get all of
them to add together.  However, with that said, I'd argue that it's
bad design to do that at all.  I'd recommend instead taking an array
parameter and adding the elements of the array.  Not only is it
cleaner and easier to understand, it also solves the problem of
extending that functionality (so you're not duplicating the
func_get_args in each child)...



Personally, I see this as practically a non-issue.  You shouldn't be
using inheritance that much anyway.  Always favor composition over
inheritance.  There are cases where inheritance is good, but the vast
majority of the times it's actually an anti-pattern.  It creates
tight-coupling and really ugly and hard to maintain code.  Look at the
design patterns demonstrated in the popular books and websites.  Do
any of them use inheritance?  Nope...  Now I know this is a little bit
beyond the scope of this discussion, but I felt it was important to
state since the majority of the justifications for allowing loose
method inheritance and stop the runtime from enforcing inherited
method signatures would be better rectified by switching from
inheritance to composition. This is especially true in the
Square-Rectangle problem cited earlier.

Also, remember that classes should be open for extension, but closed
for modification (the Open-Closed principle in SOLID design).  And
since when extending an interface you can't change the signature of a
method, it stands to reason that in order to uphold the Open-Closed
principle, method signature can't change in an interface. It's a
slight leap to extend that to method signatures should change for the
same exact reason.  And we can remove that leap once we see that a
regular class's signature is an interface and as such is a contract as
well.  So we can't change the signature without violating the contract
and changing the API.  This works for all non-private methods, since
the method is open for extension, and as such is really declaring its
own personal interface.  For private methods, you can't override it
anyway, so that's a non-issue from the start...

That's my $0.02.  I say leave it as is.  The way it is working right
now promotes good API design and makes difficult doing things that
have significant implications (especially in maintainability).  And
seeing as there's no good way to do it without language support,
removing it will just weaken the language as it removes the ability
from doing design-by-contract at all...

Anthony


On Mon, Sep 19, 2011 at 7:25 AM, Laruence <larue...@php.net> wrote:
> 2011/9/19 Etienne Kneuss <col...@php.net>:
>> Hi,
>>
>> On Mon, Sep 19, 2011 at 12:40, Etienne Kneuss <col...@php.net> wrote:
>>
>>> Hi,
>>>
>>> On Mon, Sep 19, 2011 at 12:18, Gustavo Lopes <glo...@nebm.ist.utl.pt>wrote:
>>>
>>>> Em Mon, 19 Sep 2011 10:56:03 +0100, Etienne Kneuss <col...@php.net>
>>>> escreveu:
>>>>
>>>>
>>>>
>>>>> Apparently you guys are speaking about the initial implementation of an
>>>>> abstract method, while I was talking about overriding a method, which is
>>>>> not the relly same. So the above doesn't really apply.
>>>>>
>>>>> The initial implementation of an abstract method should match the
>>>>> signature,
>>>>> while overriding a method should be able to loosen the precondition in
>>>>> many ways (type hints change, less arguments, etc..), IMO.
>>>>>
>>>>>
>>>> I should like to hear why. As far as I can see, there's absolutely no
>>>> difference. All I've seen in this thread to this respect are semantic
>>>> pseudo-arguments.
>>>>
>>>
>>> Well it is about semantics, and IMO defining a method as abstract is some
>>> sort of declaration, and this declaration should be respected when the
>>> method is actually implemented.
>>>
>>> On the other hand, interfaces define usage capabilities, and those usages
>>> should work.
>>>
>>> There might be close to no difference in the way the are internall handled
>>> currently, but IMO there is a semantic difference between the two.
>>>
>>
>> Given that the discussion has now gone into many directions:
>> - constructors/normal methods
>> - abstract/interfaces/overriding
>>
>> Let me write some small RFC describing what we currently do w.r.t. prototype
>> checks, and also summarize what people propose as changes to them. We can
>> then discuss those and eventually vote for the way to go forward.
> That will be nice, thanks.
>>
>>
>>>
>>>
>>>> I'd say interfaces are much more likely to include more useless parameters
>>>> than a concrete method definition, which most likely will only include the
>>>> arguments it actually needs.
>>>>
>>>> An example:
>>>>
>>>> http://www.google.com/**codesearch#HmA4mAI_aLc/src/**
>>>> main/java/terrastore/server/**impl/support/**JsonBucketsProvider.java&q=*
>>>> *implements%5C%**20MessageBodyWriter&type=cs&l=**36<http://www.google.com/codesearch#HmA4mAI_aLc/src/main/java/terrastore/server/impl/support/JsonBucketsProvider.java&q=implements%5C%20MessageBodyWriter&type=cs&l=36>
>>>>
>>>> This is the most common scenario for implementations of this interface
>>>> (see the other search results).
>>>
>>>
>>>> --
>>>> Gustavo Lopes
>>>>
>>>> --
>>>> PHP Internals - PHP Runtime Development Mailing List
>>>> To unsubscribe, visit: http://www.php.net/unsub.php
>>>>
>>>>
>>>
>>>
>>> --
>>> Etienne Kneuss
>>> http://www.colder.ch
>>>
>>
>>
>>
>> --
>> Etienne Kneuss
>> http://www.colder.ch
>>
>
>
>
> --
> Laruence  Xinchen Hui
> http://www.laruence.com/
>
> --
> PHP Internals - PHP Runtime Development Mailing List
> To unsubscribe, visit: http://www.php.net/unsub.php
>
>

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to