All: I'll be continuing work on the RFC tomorrow, it is still in draft.

Yasuo: I'll read through your notes tomorrow, thanks for detailed input.

Cheers
Joe

On Sat, Feb 14, 2015 at 9:17 AM, Yasuo Ohgaki <yohg...@ohgaki.net> wrote:

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

Reply via email to