Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:114155 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 30755 invoked from network); 25 Apr 2021 17:49:08 -0000 Received: from unknown (HELO php-smtp4.php.net) (45.112.84.5) by pb1.pair.com with SMTP; 25 Apr 2021 17:49:08 -0000 Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 6E4A61804DF for ; Sun, 25 Apr 2021 10:52:58 -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_H3,RCVD_IN_MSPIKE_WL,SPF_HELO_NONE, SPF_PASS autolearn=no autolearn_force=no version=3.4.2 X-Spam-Virus: No X-Envelope-From: Received: from mail-ot1-f42.google.com (mail-ot1-f42.google.com [209.85.210.42]) (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, 25 Apr 2021 10:52:57 -0700 (PDT) Received: by mail-ot1-f42.google.com with SMTP id e89-20020a9d01e20000b0290294134181aeso24149387ote.5 for ; Sun, 25 Apr 2021 10:52:57 -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; bh=MSWpItpTE9PHWrgmK2L+I2nzzlYDJeOjxs/HWIfF2R4=; b=SJUhU/wuak0ShcweEcC0CAzlk7odWmge46HmfBVcIh13om8BGciV7pcNTG8bZNRQho UJm0BcFfqNTmOebYxnUppU/2kfrCwUXhgIASQa27GJyGSY4AK/ISij+A0zhgvJcFD8t0 ntUFXa41Idxo9Bb+r0MF+N9+lShsMwZHwFELrKLx1cBvFAho2mJaWkoEw1fq46woqAxw aNmoM/bolA2wBzPfsOeFf+3pCf3kDymfGd7rfeIe7o303QkTkF7I9/ZYTtGp/GzV/6o+ suf60YyIo+Tdza94u8m19Di+mDexAN3pJDZboA4AiECVXnp+v7HUxsxJ5EkElGnRbjfR i4Qg== 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; bh=MSWpItpTE9PHWrgmK2L+I2nzzlYDJeOjxs/HWIfF2R4=; b=EeNYFgteRpJDlfqUZktUOzSImIhSTujCRnm6S58+6xKY5rKXYaNlXGroRcx2BNSIdZ 1I9zTxuieOHbbCsF3JkDO5E1/UgDCfmrN82RRaJYIVztSCoJSus8RsF2racXYew8v9L4 RkjxPm2CjFy5/URRpr5KYptRGmD6RgTYSq5p8j4hpJaThFV1eB1B1aVWxLAFRbJ2A7Z1 5P8yTUxcM1ykbYHraffnL+3uqqdWeIGk3W61JpNStsERJVeAKXTZIT049jhQNwmfZ0a8 /jH0ddTpahXkpymDdPn0wZ0NbRbsLP9sqegZAW2s7DpQXWZColmQIq4thZ9arTravjrR M/Qg== X-Gm-Message-State: AOAM531YTM1DZRKiKAirUjAxRm7nU+8atDd/B/+acU73EdBrl+pUEN9P ie+RHN9OvoKJqlLMGTJoFsd8xQD+TmGnGNn+6uP997JaKKA= X-Google-Smtp-Source: ABdhPJxcN9Nr+G09ph2CIek55gXtwUOYFGoQJl98RLwUVK4Wbl0VFg2tME42Atb5aVIu24FEE36P0JPQYPkhh1hdVdg= X-Received: by 2002:a9d:1ea9:: with SMTP id n38mr11802205otn.233.1619373175929; Sun, 25 Apr 2021 10:52:55 -0700 (PDT) MIME-Version: 1.0 References: <5b9f1500-615a-48f1-815f-1d48b327ef90@processus.org> <179049b1475.11134368b213512.254739612773841999@void.tn> <0BF84585-DDC3-4B25-BFD2-D8B916D135EE@newclarity.net> In-Reply-To: <0BF84585-DDC3-4B25-BFD2-D8B916D135EE@newclarity.net> Date: Sun, 25 Apr 2021 18:52:45 +0100 Message-ID: To: PHP internals Content-Type: multipart/alternative; boundary="000000000000e8fec905c0cfb230" Subject: Re: [PHP-DEV] [RFC][Draft] Sealed Classes From: davidgebler@gmail.com (David Gebler) --000000000000e8fec905c0cfb230 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Still, all these problems are solved to the same degree if you add a #[Sealed] attribute to a class which has no functional impact. You have sufficiently indicated to any user that extending this class is not a designed feature and may cause backwards-incompatible breaks with future releases - in a way that both a programmer and IDE can reason about, which in PHP's context is what matters. Attributes arguably even have a greater qualitative advantage that they can be applied right down as far as individual method parameters. In Java the idea of final and sealed classes makes more sense, since we actually to some extent need the compiler to be able to reason about these types and can gain optimization and code generation benefits from its being able to do so. PHP's concept of typing and implementation of type checking as an interpreted language is completely different. I wonder, if final and sealed as language constructs really offer the guarantees about intent and safety their advocates say they do, why are they not the default? Why can no one point me to a language where I have to write something like extendable class Foo permits all { .... } (and there are people who would be in favour of making inheritability this explicit, but I'm not one of them) It's one thing as an author of code to say "I only intended and support this finite set of use-cases", it's quite another to say "and you should be impeded from proceeding with any legitimate use-case I didn't imagine or foresee" In practice, the only thing I've ever seen this achieve is to create difficulties, while the claimed benefits can be adequately (and better) achieved through existing patterns like annotations, interfaces and DI. On Sun, Apr 25, 2021 at 4:36 PM Mike Schinkel wrote: > > > On Apr 24, 2021, at 7:39 PM, David Gebler wrote: > > I don't love this idea, I'm not very fond of the final keyword, either; > > > I'll start by saying the final keyword caused me a tremendous amount of > heartache because it was used on a class in a framework that I badly, bad= ly > needed to extend. > > But even so, I recognize why they used it, and I still don't have a great > argument for how they could address the reasons they used it some other w= ay. > > I've always believed annotations (or attributes in PHP these days) are a > better of way of indicating you, as an author of a class, did not write i= t > with inheritability in mind or intended than restricting language feature= s > through syntactic constructs. > > The RFC says "when you have a class in your code base that shares some > implementation detail between 2 or more other objects, your only protecti= on > against others making use of this class is to add `@internal` annotation, > which doesn't offer any runtime guarantee that no one is extending this > object", to which I ask - why do you need this guarantee? What does it > qualitatively add? If I make a judgement that I want to extend your class > or implement your interface, I can just delete the sealed keyword from yo= ur > code and carry on. So it doesn't actually offer any guarantee at all that > I'm not extending the type. > > > Actually, it does offer such a guarantee. It guarantees if you are using > a non-forked version of the original developer's (OD's) library or > framework then that class won't be extended. When someone pulls the > original non-forked version from its source repository =E2=80=94 such as = when using > Composer =E2=80=94 then that code will be (effectively) guaranteed not to= be > extended. > > OTOH, if you do delete the sealed (or final) keyword you have then forked > the code, in a defacto manner if not a literal one. If you use a forked > version of the code, you now own the maintenance of that code and any bug= s > that are generated by your forked changes in using code. The original > developer has no moral, ethical or even contractual obligation to care > about the breakage you cause. > > Hypothetical example: You fork the code, remove sealed/final, then > subclass the code and add a method, let's call it ToString(). And you wri= te > your application to use ToString(). Now the OD releases a new minor versi= on > and they also add a ToString() method. Applications using your fork > probably cannot use the new version of the OD's library because when the > library calls ToString() your version is called. So you have to update yo= ur > application to use the new version of the library and once again remove > sealed/final. > > AND, if your code is instead another add-on library, now users of your > add-on library will also have to fix their code too. Which could > potentially be a large number of users if your add-on is successful. > > So not using final or sealed can result in some really hairy and possibly > impossible to fully resolve backward compatibility concerns for developer= s > who publish libraries and/or frameworks. > > The best it can achieve is to indicate your > intentions, which I believe can be adequately done today through an > attribute, no addition to the language needed. > > > Still, I concur with your concerns. Developers too often implement final > classes in libraries and frameworks without fully addressing all the > use-cases and/or adding enough extensibility points because it makes thei= r > lives easier. Because of that final =E2=80=94 and sealed, if added =E2= =80=94 can make the > life of an application developer a living hell. > > So what's the answer? I don't know that I have the ultimate answer, but = I > would be a lot more comfortable with adding features to PHP such as ones > like sealed that restrict the "O" in S.O.L.I.D.[0] if PHP were to offer t= he > following three (3) things, all of which can be found in Go, and I am sur= e > other languages: > > 1. Class embedding[1] =E2=80=94 Allows one class to embed another and imm= ediately > have access to all its properties and methods, and also to be able to > extract an instance of that embedded class. It is called "Type embedding= " > in Go. > > 2.Type definitions[2] =E2=80=94 A typedef would allow developers to defin= e > constrained versions of existing types, such as `FiveStarRating` which > could only contain 1, 2, 3, 4 or 5, or types that identify a signature, f= or > example as `ConvertToString` which could require a closure that implement= s > `func(mixed):string`. In Go you can compose other types to create new > types, but I'm not sure if those other type of types could apply to PHP, = at > least as it currently exists, and especially because it is not a compiled > language. > > 3. Structural typing[3] =E2=80=94 Basically interfaces that can be implem= ented > implicitly rather than explicitly. For example, if I wanted to implement= a > Stringable interface that requires a ToString():string method then > structural typing would allow me to implement that interface simply by > adding a ToString() method instead of requiring me to also add "implement= s > Stringable" to the class definition. > > Those three features are all killer language features and would make grea= t > additions to PHP. IMO, of course. > > #fwiw > > -Mike > > [0] https://stackify.com/solid-design-open-closed-principle/ > [1] https://travix.io/type-embedding-in-go-ba40dd4264df > [2] https://go101.org/article/type-system-overview.html > [3] > https://blog.carbonfive.com/structural-typing-compile-time-duck-typing/ > --000000000000e8fec905c0cfb230--