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