Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:82676 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 91509 invoked from network); 14 Feb 2015 09:18:40 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 14 Feb 2015 09:18:40 -0000 Authentication-Results: pb1.pair.com smtp.mail=yohgaki@gmail.com; spf=pass; sender-id=pass Authentication-Results: pb1.pair.com header.from=yohgaki@gmail.com; sender-id=pass Received-SPF: pass (pb1.pair.com: domain gmail.com designates 209.85.216.179 as permitted sender) X-PHP-List-Original-Sender: yohgaki@gmail.com X-Host-Fingerprint: 209.85.216.179 mail-qc0-f179.google.com Received: from [209.85.216.179] ([209.85.216.179:47682] helo=mail-qc0-f179.google.com) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id 12/00-25612-EE21FD45 for ; Sat, 14 Feb 2015 04:18:38 -0500 Received: by mail-qc0-f179.google.com with SMTP id r5so17604882qcx.10 for ; Sat, 14 Feb 2015 01:18:35 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:sender:in-reply-to:references:from:date:message-id :subject:to:cc:content-type; bh=CWuuSHc8gekmp0t4we0tpjn5Aa6PIQfcVw4gKhQawRE=; b=Ke8TWtCSPnsqboe6McztmrLUtl+7hA7VnpAIWLmgLJouIdx7YGJoUIQ6OqaFR6oRQ7 DXFKV+Xg8CPAthjNLUkbCeYTke/ynraahkimTqtaUimG6vovWA0r/qqYW7S89QFCyFlY NUNbyVh76PxDzYPcgPaxpg6oHJB0ZeHcpkmA+WlcZYtfOE8phoKPAYGJgR8Dy9dA7/dL Z0TggOvXSdYwxSGGsOyjEoNHdnmDKeHuil7/fCNHjWB0ZfaU6BMIS3Dw/pF0pJoqeVZ/ HyyP2z1ceDEs60F8GiOr0/MdZW+gMClTXMDdpLWMAlc/M1CIqQPzxkLgzWacrJkwoaRG lT7Q== X-Received: by 10.229.190.6 with SMTP id dg6mr35087267qcb.16.1423905515073; Sat, 14 Feb 2015 01:18:35 -0800 (PST) MIME-Version: 1.0 Sender: yohgaki@gmail.com Received: by 10.229.198.8 with HTTP; Sat, 14 Feb 2015 01:17:54 -0800 (PST) In-Reply-To: <013801d0481d$d34c5170$79e4f450$@php.net> References: <54DAFD32.3000005@gmail.com> <54DB0BC0.20304@gmail.com> <54DBA801.8060403@gmail.com> <011e01d04802$cbd78ce0$6386a6a0$@php.net> <013801d0481d$d34c5170$79e4f450$@php.net> Date: Sat, 14 Feb 2015 18:17:54 +0900 X-Google-Sender-Auth: HTlJkhwyBQ3ep223fBnvIjvAATg Message-ID: To: francois Cc: Dmitry Stogov , Joe Watkins , Stanislav Malyshev , PHP Internals Content-Type: multipart/alternative; boundary=001a113367986b772b050f08d676 Subject: Re: [PHP-DEV] Design by Contract From: yohgaki@ohgaki.net (Yasuo Ohgaki) --001a113367986b772b050f08d676 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable 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=C3=A7ois Laupretre 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=E2=80=99t ask why, I don= =E2=80=99t understand the reason very well. But that=E2=80=99s 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 don= e 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=E2=80=99t check parent=E2=80=99s p= re-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=E2=80=99s responsibility to loosen cond= itions in derived classes but it is possible. If you check every parent=E2=80=99s 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 >=3D 0 (Human) The base class. Followings are children. age >=3D 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 =E2=80=98PHP way=E2=80=99 (something like the way constructors and destruct= ors explicitly call their parent methods). My logic is : if the method we are entering has no pre-conditions, we don=E2=80=99t check anything (don=E2=80=99t search a = parent method). If it defines some, we execute them. I introduce a =E2=80=98special=E2=80= =99 condition : the =E2=80=98@parent=E2=80=99 pseudo-condition means =E2=80=98go down check= ing my parent=E2=80=99s conditions=E2=80=99. This way, similar to =E2=80=98parent::__construct()=E2= =80=99 logic allows the developer to decide if he wants to check parent=E2=80=99s 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=E2=80=99s pre-conditions > > * @requires =E2=80=A6 > > > > 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=E2=80=99ll assume that =E2=80=98@parent= =E2=80=99 means =E2=80=98check the conditions defined in the parent method and the method of the interface that defines it=E2=80=99. Unfortunately, interfaces have parents too and ca= n define methods with same name. So, it=E2=80=99s the same sort of problems as multi= ple inheritance. I will need to make a choice here. The simplest one would be =E2=80=98no support for DbC in interfaces=E2=80=99. 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 yohgaki@ohgaki.net --001a113367986b772b050f08d676--