Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:82689 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 26565 invoked from network); 14 Feb 2015 15:17:42 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 14 Feb 2015 15:17:42 -0000 Authentication-Results: pb1.pair.com header.from=pthreads@pthreads.org; sender-id=unknown Authentication-Results: pb1.pair.com smtp.mail=pthreads@pthreads.org; spf=permerror; sender-id=unknown Received-SPF: error (pb1.pair.com: domain pthreads.org from 209.85.220.45 cause and error) X-PHP-List-Original-Sender: pthreads@pthreads.org X-Host-Fingerprint: 209.85.220.45 mail-pa0-f45.google.com Received: from [209.85.220.45] ([209.85.220.45:35577] helo=mail-pa0-f45.google.com) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id 88/63-03565-5176FD45 for ; Sat, 14 Feb 2015 10:17:42 -0500 Received: by mail-pa0-f45.google.com with SMTP id rd3so18425226pab.4 for ; Sat, 14 Feb 2015 07:17:39 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:in-reply-to:references:date :message-id:subject:from:to:cc:content-type; bh=DubL5uZcSXHeptg7xDoJ3tilSem/vnqoxh0jOCjpHCQ=; b=iqS3C0sqHPQ1SFFkwaluHcD9O0fzrF+oDCqu6W9tdxngeHNo+ZbcScc69BMAps+ndK IEFn8qQzpdrZPNEk8BE2GF8/QAOFXQ0RnEAqUrgKr0mgKCxSIEaTJQbZzXGQw3MCOnts OQTjQ/Y12kd0GUMSMPbq3v3MFaexw1/pBf3l6cgSl5vrmIfIP7bZ0FjHUmMmvuwYJEdd A0MSf2Ln33A0eWpivmm9pVZGrQ+W8dBaYKzavhzi/LXZ1son3hB/5KtUPRVMpPLYaWKM Tf1zniWkY8x0UzCtMff0qPQk4oZdggM5Qs3RLC/61Kj1ZrJhtBPAK6zOwjS0M84ZnXbz PZHg== X-Gm-Message-State: ALoCoQl19UGrQrJENgAnMglD0MIGo0dt9qLqFiRNQWKEkixFX5jp9FwT37YLF90vmdEcTh87+gFN MIME-Version: 1.0 X-Received: by 10.66.185.230 with SMTP id ff6mr24766141pac.102.1423927058964; Sat, 14 Feb 2015 07:17:38 -0800 (PST) Received: by 10.70.49.100 with HTTP; Sat, 14 Feb 2015 07:17:38 -0800 (PST) X-Originating-IP: [86.190.233.59] In-Reply-To: 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 15:17:38 +0000 Message-ID: To: Yasuo Ohgaki Cc: francois , Dmitry Stogov , Stanislav Malyshev , PHP Internals Content-Type: multipart/alternative; boundary=001a1134259a893a68050f0dda69 Subject: Re: [PHP-DEV] Design by Contract From: pthreads@pthreads.org (Joe Watkins) --001a1134259a893a68050f0dda69 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable 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 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=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 everyon= e 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 paren= t > 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= 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-condition= s > 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 co= nditions > in derived classes but it is possible. If you check every parent=E2=80=99= 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 >=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 destru= ctors explicitly > call their parent methods). My logic is : if the method we are entering h= as > 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 che= cking 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 n= ot. > > > > 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 defin= ed > but it does not explain when they are checked. I assume this is considere= d > as a parent but the order matters in pre-conditions as we only execute th= e > first conditions we find. I think I=E2=80=99ll assume that =E2=80=98@pare= nt=E2=80=99 means =E2=80=98check > the conditions defined in the parent method and the method of the interfa= ce > that defines it=E2=80=99. Unfortunately, interfaces have parents too and = can define > methods with same name. So, it=E2=80=99s the same sort of problems as mul= tiple > 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 > --001a1134259a893a68050f0dda69--