---- On Sun, 25 Apr 2021 22:56:26 +0100 Chase Peeler <chasepee...@gmail.com> 
wrote ----

 > On Sun, Apr 25, 2021 at 11:36 AM Mike Schinkel <m...@newclarity.net> wrote: 
 >  
 > > 
 > > 
 > > > On Apr 24, 2021, at 7:39 PM, David Gebler <davidgeb...@gmail.com> 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, 
 > > badly 
 > > 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 
 > > way. 
 > > 
 > > > 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 
 > > it 
 > > > with inheritability in mind or intended than restricting language 
 > > features 
 > > > 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 
 > > protection 
 > > > 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 
 > > your 
 > > > 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 — such as when 
 > > using 
 > > Composer — 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 bugs 
 > > 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. 
 > > 
 >  
 > I'd argue that if the original developer made clear that you shouldn't 
 > extend a class, then they still have no moral, ethical, or even contractual 
 > obligation to care about the fact you've used the object in a way they were 
 > clear was not supported. 
 >  
 > I'm with David on this one. I can understand the need to enforce a 
 > final/sealed concept for core functionality implemented in C which might do 
 > some funny things under the hood. I don't think that should be extended to 
 > userland. If you want to warn someone, that's fine. But don't totally 
 > prohibit them. 
 >  
 > Given the ability for composer to pull from forked repos and the easy of 
 > keeping a forked repo in sync with it's upstream version, creating a fork 
 > just to remove a sealed/final designation isn't that difficult to do. 
 >  
 >  
 > > 
 > > Hypothetical example:  You fork the code, remove sealed/final, then 
 > > subclass the code and add a method, let's call it ToString(). And you 
 > > write 
 > > your application to use ToString(). Now the OD releases a new minor 
 > > version 
 > > 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 
 > > your 
 > > 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 developers 
 > > 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 their 
 > > lives easier.  Because of that final — and sealed, if added — 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 
 > > the 
 > > following three (3) things, all of which can be found in Go, and I am sure 
 > > other languages: 
 > > 
 > > 1. Class embedding[1] — Allows one class to embed another and immediately 
 > > 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] — A typedef would allow developers to define 
 > > constrained versions of existing types, such as `FiveStarRating` which 
 > > could only contain 1, 2, 3, 4 or 5, or types that identify a signature, 
 > > for 
 > > example as `ConvertToString` which could require a closure that implements 
 > > `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] — Basically interfaces that can be implemented 
 > > 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 "implements 
 > > Stringable" to the class definition. 
 > > 
 > > Those three features are all killer language features and would make great 
 > > 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/ < 
 > > https://blog.carbonfive.com/structural-typing-compile-time-duck-typing/> 
 >  
 >  
 >  
 > -- 
 > Chase Peeler 
 > chasepee...@gmail.com 
 > 

> I can understand the need to enforce a final/sealed concept for core 
> functionality implemented in C which might do some funny things under the 
> hood. I don't think that should be extended to userland. If you want to warn 
> someone, that's fine. But don't totally prohibit them.

This is not about doing "funny things under the hood", this is about declare a 
possible sub-types for a specific type.

Taking the `ResultInterface` example from the RFC, there can only be 2 sub 
types for it, `Success` or `Failure`, so when a function declares it's return 
type as `ResultInterface`, you know you are getting either one of these two 
data types, any nothing else.

If the interface would be unsealed, that guarantee doesn't exist anymore, and 
the library which might contain functions that operate on the Result type would 
become broken as now they have another type that is not being handled when 
unwrapping ( hence why Psl doesn't actually contain the `unwrap` function 
mentioned in the RFC ).

> given the ability for composer to pull from forked repos and the easy of 
> keeping a forked repo in sync with it's upstream version, creating a fork 
> just to remove a sealed/final designation isn't that difficult to do.

Yes, you can, but i don't think any maintainer is willing to waste their time 
looking into a weird bug resulted from new types that you created by unsealing 
a sealed class/interface.

If you ever had to do this, then the library you are using is flawed, as it 
should offer you a way to extend things when inheritance makes sense, but 
sometimes it doesn't ( e.g there's no reason why someone would want a new 
`Result` type that is not `Success` or `Failure`, or would want to implement 
the `ExceptionInterface` that is used within a library )

for services, I prefer to have implementations declared final, but also offer 
an unsealed interface to make it possible for people to decorate the 
implementation or rewrite their own.

If you find yourself in need to extend a class that is declared final, decorate 
it instead, and if there's no interface for it, maybe send a PR, forking to 
remove final is not the solution.

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php

Reply via email to