Hi,

On Mon, Sep 19, 2011 at 16:26, Anthony Ferrara <ircmax...@gmail.com> wrote:

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

Uh unless I'm misunderstanding you here there is something that looks
terribly wrong:

Overriding methods with a  different signature, e.g. allowing more sets of
arguments(contra variant args), or returning a more precise type (covariant
return type) definitely works with LSP, and it is fine in OO. LSP only
states about substitution between one parent and its child, not one child
with another...


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


Well, we have no way of declaring that we accept an undefined number of
arguments. So there is simply no choice here.


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

By doing that, you also do exactly what you describe earlier, you push the
args checks in the code itself, as you could always pass an incomplete
array, and you could error out earlier.

Var-args functions have been quite used for many things, there is no reason
why we should flag that as bad practice now... is there?


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



-- 
Etienne Kneuss
http://www.colder.ch

Reply via email to