Hi Francois, Now I understand what you are discussing. Since we may have stricter typing, we probably better to consider type safety theory even if PHP is weakly typed.
What I'm going to write is not type theory, though. On Sat, Feb 14, 2015 at 3:16 PM, François Laupretre <franc...@php.net> wrote: > > The theory (from Eiffel guru) states that, to respect this fucking LSP rule, the pre-conditions to check when entering a method must be less strict than when entering its parent method. Don’t ask why, I don’t understand the reason very well. But that’s his rule, and everyone seems to respect it. > > > > In your RFC, you say that, when we enter a method, we must check its pre-conditions, as well as the ones of every parents. As you can only add conditions, compared to what you would do for the parent, the checks can only be stricter (more conditions). > Pre/Postconditions should be checked only when parent method is called. That's what Eiffel does. Eiffel does not allow method overriding. Therefore, pre/postconditions of methods (not invariants. Invariants inherited implicitly(automatically) both Eiffel and D are evaluated except certain methods) cannot be evaluated automatically unless parent method is called. public function foo() { parent::foo(); // pre/post conditions are evaluated upon call } Since children's method may have whole different way of handing parameters, including swapping parameter order, adding/removing parameters, etc. Parameters cannot be checked automatically unless we have some kind of binding system that bind child parameter to parent parameter. Even if we have it, parameter for parents may be generated in function body. It cannot be perfect. Therefore, overridden method's parent precondition evaluation cannot be done until child calls it explicitly. Postcondition is the same. Child object may return whatever return value it returns, there is no point checking parent method's postcondition automatically. Invariant are also pre/postcondition, but it differs. > The logic described in the D documentation is : if a method defines pre-conditions, check them and stop (don’t check parent’s pre-conditions). If the method does not define pre-conditions, go down one level and check if parent method defines some. As soon as a method defining pre-conditions is found, these are checked and control is returned without going further. This way, it is still the developer’s responsibility to loosen conditions in derived classes but it is possible. If you check every parent’s pre-conditions, it is just *not* possible. > Basic rule is we shouldn't be able to modify parent contracts(invariant, methods pre/postconditions). If we can change it, it's the same as changing type. Your discussion applies to invariant and this is what you write, I suppose. Child only can strengthen contract(invariant) e.g. age >= 0 (Human) The base class. Followings are children. age >= 18 (Adult) 18 or over is greater than 0. OK age <18 (Child) 0 to 18 are greater than 0. OK age < 0 (Alien) This cannot happen as Human subtype. It violates Human type Type safety is protected by invariant like this. > > I am not sure I am clear. > > > > I chose not to follow exactly this logic as I think we can do it more ‘PHP way’ (something like the way constructors and destructors explicitly call their parent methods). My logic is : if the method we are entering has no pre-conditions, we don’t check anything (don’t search a parent method). If it defines some, we execute them. I introduce a ‘special’ condition : the ‘@parent’ pseudo-condition means ‘go down checking my parent’s conditions’. This way, similar to ‘parent::__construct()’ logic allows the developer to decide if he wants to check parent’s conditions or not. > We should only evaluate method's contract(pre/postcondition) when it is called. We should always evaluate class contract(invariant) including parents when it is applicable. (exceptions are __construct/_destruct/etc) > > > Example : > > > > /** > > * @requires ($a < $b) > > * @requires @parent <- Go checking parent’s pre-conditions > > * @requires … > > > > For better consistence, I chose to implement the same for post-conditions and invariants, but I am not sure I will keep > > the same logic. > > > > The only thing that remains not clear for me is the role of conditions defined in interfaces, as the D documentation says that they can be defined but it does not explain when they are checked. I assume this is considered as a parent but the order matters in pre-conditions as we only execute the first conditions we find. I think I’ll assume that ‘@parent’ means ‘check the conditions defined in the parent method and the method of the interface that defines it’. Unfortunately, interfaces have parents too and can define methods with same name. So, it’s the same sort of problems as multiple inheritance. I will need to make a choice here. The simplest one would be ‘no support for DbC in interfaces’. The same rule for class applies to interfaces. We should only evaluate method's contract(pre/postcondition) when it is called. We should always evaluate class contract(invariant) including parents when it is applicable. (exceptions are __construct/_destruct/etc) /* contracts are omitted */ class B extends A { function __construct() { parent::__construct() } } When constructor is called, these are the detailed order. B::__construct() precondition evaluation B invariant evaluation skipped. Constructor is special. A invariant evaluation skipped. Constructor is special. /* function body of B. Calls parent::__construct() */ A::__construct() precondition evaluation /* function body of A */ A::__construct() postcondition evaluation /* function body of B.*/ A invariant evaluation B invariant evaluation B::__construct postcondition evaluation Developers can ignore parent constructor pre/postconditions. If it does matter to developers, it's okay. PHP( and other languages) allows whatever bad design. We cannot ignore parent class invariants. If developer violate parent property restrictions, it's violation of parent class type and this must be forbidden. It's checked by invariant contract. I don't think I explained well, but you might be able to understood me. Keeping it simple works. We need no special handlings. Regards, -- Yasuo Ohgaki yohg...@ohgaki.net