Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:115748 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 28126 invoked from network); 16 Aug 2021 04:13:29 -0000 Received: from unknown (HELO php-smtp4.php.net) (45.112.84.5) by pb1.pair.com with SMTP; 16 Aug 2021 04:13:29 -0000 Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 883331804B4 for ; Sun, 15 Aug 2021 21:45:21 -0700 (PDT) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM,HTML_MESSAGE, RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_PASS autolearn=no autolearn_force=no version=3.4.2 X-Spam-ASN: AS15169 209.85.128.0/17 X-Spam-Virus: No X-Envelope-From: Received: from mail-lj1-f171.google.com (mail-lj1-f171.google.com [209.85.208.171]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (P-256) server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Sun, 15 Aug 2021 21:45:21 -0700 (PDT) Received: by mail-lj1-f171.google.com with SMTP id q21so3719036ljj.6 for ; Sun, 15 Aug 2021 21:45:21 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=mime-version:references:in-reply-to:from:date:message-id:subject:to :cc; bh=mGPl6eij/qhUB4bXd5zTRc+sezsOniZeThIMX4XMNI0=; b=OnaKcZPE0P9b8DZI1KynsttLA9v9aRldl/dSzBwNunjowzj+8GqmL8QH3hb4WTkMjA 2T5uy8QBzeEvxgtUMworbpPUMmFZni8jECEktUCi8ZSt6bDGS3nTnZF9sMdkKLV7hrkl CyF2fmFWspJSo/0frLSklozLDUTvH9LSvfexcSBvnFNFd21bzPMtpoNkbhAOn53JmYW6 ET/B8+MblRQY32tQZFzIbr4S1e3gZnK1qACyPulranxxEFnepxeEFnIIxx/7fMJQq9LX jh4NPrpNLZULuJw6sihRe85/yLXVlcTvoqS8Hm+K6jcHDtUeUTFHmt4G4QPQET4zPGt8 5pHA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc; bh=mGPl6eij/qhUB4bXd5zTRc+sezsOniZeThIMX4XMNI0=; b=KP5fsrRVPfAwd2hktLtSpZxYW/igT6weDX1D9gX6A70bXkqVT4OIOIg9qRhCeHnRyx 8y0sqoLsxR0LA2dul9WIzxVDqQKYoqA8GOk0Di2rT/P8HHI+PWRMG0COTHO2Oxkk27wG UbHPadVcL0H47KRXkIPl8LqtfJu/GQj2VJO5nJKJIxHS4c66SAdWD+Fi5jeZ5iI9+8sf neZK4oe5pI24q2ukqSMQ1Z5OqLZ6XXxqafLvT3GpqnA9ek512S6TH693noGZE1DN9/wL On9Ndn4VmZCUbCWmoDOXgO/5zS14gQ9B2oHB4JQzCdAWGt4fAKW9KMLaxBp8mdqu7vr9 Dc5A== X-Gm-Message-State: AOAM533qpGV7oxQA8Aqx4VVbgUIhbalAUV6t5ogjWPhVzylukEshYaeq fFqPanoRIpW0ecP35ihWQWiuDa4fcI0so4iRSeM= X-Google-Smtp-Source: ABdhPJxpdMP673IDcJgKB8nLhizZKbRA53JsFOdW48Y+v/QYtilOogDODcNRWbSrIMbY1EiQ8YTJRxsA7OLApIJprEE= X-Received: by 2002:a05:651c:891:: with SMTP id d17mr10928331ljq.24.1629089119428; Sun, 15 Aug 2021 21:45:19 -0700 (PDT) MIME-Version: 1.0 References: In-Reply-To: Date: Sun, 15 Aug 2021 21:45:13 -0700 Message-ID: To: =?UTF-8?Q?Alexandru_P=C4=83tr=C4=83nescu?= Cc: PHP internals Content-Type: multipart/alternative; boundary="0000000000004563c405c9a5de11" Subject: Re: [PHP-DEV] [RFC] Never For Argument Types From: jordan.ledoux@gmail.com (Jordan LeDoux) --0000000000004563c405c9a5de11 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable On Sun, Aug 15, 2021 at 4:06 PM Alexandru P=C4=83tr=C4=83nescu wrote: > Hey Jordan, > > From type perspective, this sounds good. > > But, in my view, types should be checked statically. > For inheritance, when defining a method, both the parameters and return > types are nicely checked statically and that's good. > On the other side, when calling a method, due to the dynamic nature of > PHP, there is no simple way of checking it statically. > > For example, if an interface is defining a method with a parameter of typ= e > string and an implementation is changing the parameter type to a string|i= nt > union, the caller that have a knowledge about the interface (that accepts > only string) can call it with an integer and everything will work fine. > It's kind of the same situation and this will too be flagged by the IDEs > and static analysis tools. > From a usage design perspective, this is bad, in my view. If caller knows > about the interface only, that is to ensure decoupling and this breaks it= . > > I was hoping that, at some point, we would be able to validate that > arguments types match parameters types statically, to avoid the continuou= s > runtime cost for it. > Maybe this can already be done, with some changes, when arguments are > properties defined with types. > When variables will have types and/or when variables will be > "final"/"constants", more static checks could be implemented. > While that is something that I personally would not be opposed to working towards, I don't see how that is possible so long as type juggling exists. Unless PHP were to adopt a forced typing system similar to Java or C#, there will always be cases where the engine is only able to resolve types at runtime. Such an effort would doubtless result in a large reorganization of how types interact with various parts of the language, so I don't see that this would be more blocking towards such an effort than the many other things currently in the language. Any version of PHP that implemented such a plan would almost universally be backwards incompatible with previous code. Not so much a major version increment, but a parallel language syntax. Perhaps I'm incorrect on that account. I admit to having much less knowledge of that particular area of the engine than others on this list. > Adding another exception to this behavior might not be the best idea if w= e > might move towards removing the behavior. > > Coming back to the interface decoupling topic, that's exactly why > interfaces should be used, to allow polymorphism. > If I see an interface that was created just so it can be implemented by > multiple classes but the caller doesn't use the interface but it uses the > actual classes, I call it out as that is the wrong abstraction and the > interface should just be removed. > > Your example with ArrayAccess is similar with this; when using it, you > know exactly what implementation you have. The interface is not helping > here related to polymorphism but it is actually required to allow a > language construct related to the engine itself, to translate accessing a= n > object with the same syntax as for an array. > If the 4 functions would have been magic methods, without an interface, > you would not have this problem. > > Your example with CollectionInterface is exactly a place where I would sa= y > the interface is unnecessary because you already know the implementation, > since you call it with the correct type. Just keeping the classes not > having to implement the interface would work just fine. > > If you want to enforce method names on all Collection classes, you can do > that with other tools for static analysis, including code reviews; not wi= th > the Interface. > > Sorry for the long reply but my means to make it shorted are limited and > I'm using a smartphone only in this period (being in vacation). > > Alex > I think this gets towards a fundamental idea that is much more sweeping, that Mike also briefly touched on: interfaces are used currently to control access to certain language features for objects. This helps preserve these features through inheritance, but also represents problems like what you are describing. This RFC is fairly simple and straightforward. The entire patch is only 7 lines of code in two files, with the entire rest of the patch being tests. I felt this would be a more acceptable way to approach the change as it is consistent with type theory and the type system (as you noted), while being the minimal change to enable the behavior intended. The points that have been raised by you and others, including Nikic and Ondrej, are absolutely true, as I've agreed before. There are limitations and contradictions in using Interfaces in this way. However, it is also consistent with currently implemented and maintained features. Some of these, such as ArrayAccess, are currently implemented in what is fundamentally a broken way. This RFC would not make that implementation "unbroken", but would move the broken behavior from the runtime to the static analysis tools. The only real way to fix this fully, that I can see, is to not use interfaces at all for controlling engine feature access for objects. If we were to go that route, not only would this particular effort be much larger, but there would be significant BC breaks. That is... perhaps acceptable, but would certainly be more contentious. Since this was not part of my research or the original design work I did for the RFC, please understand that this is a spur-of-the-moment and arbitrary example, but it would probably result in something like: ``` class A has ArrayLike { } ``` Instead of: ``` class A implements ArrayAccess { } ``` There would be downsides to this as well, as such features *do* require an implementation of some kind of hook, and that hook *will* have different typing requirements for different implementations. If Interfaces are not the correct way to handle such hooks, and we feel it is worth the BC break and the effort to "fix" existing implementations, we would still be left with an inheritable quality that can't be directly typed against in static analysis. In reality, what I believe static analysis tools and IDEs would have to do to resolve this is to assume that any internal structure which calls for a `never` type or whatever equivalent we choose will accept a `mixed` type argument, as it can be arbitrarily widened and must be widened in order to be called successfully. It appears that the list is in agreement that this RFC is a good implementation of the type theory behind the change, but is interested in discussing the idea of addressing a much, much larger issue that this brings to the forefront. Is this an accurate summary Alex, Nikic, and Ondrej? Jordan --0000000000004563c405c9a5de11--