ramsey,
@ramsey@phpc.social avatar

If you voted against the Interface Default Methods RFC for , please take a moment to read some of the most recent mailing list replies, starting with @Crell’s here: https://externals.io/message/120725#120798

I agree this feature goes against a lot of what I’ve learned as “best practices,” but I did a lot of introspection on this and decided that this feature is good for the future of the PHP language. It unlocks a lot of potential.

Crell,
@Crell@phpc.social avatar

@ramsey The older I get, the more I think early Java did more to set back language design and OOP theory than any other system in history.

TheCodeLorax,

@Crell @ramsey I agree, but I’m struggling to identify concrete examples. For starters:

  • Methods must be on an object.
  • Lack of differentiation between Model classes and other classes.
  • Lack of Properties
  • Limited support for Dependency Injection
  • Reflection

Any others?

Crell,
@Crell@phpc.social avatar

@TheCodeLorax @ramsey

  • Java had single inheritance, so MI is evil. As we've seen, turns out interfaces and abstract classes and traits are all isomorphic if you squint.
  • Java Beans, and the proliferation of getters/setters.
  • No global functions at all.
  • Horizontal extension (not via inheritance) is/was a bitch, yet is very necessary.
  • Conflating data and logic objects.
  • Convinced a generation that OOP meant extensive ceremony.
  • Set the expectation of type systems without generics.
Crell,
@Crell@phpc.social avatar

@TheCodeLorax @ramsey And then all the languages that developed as "not Java", by throwing the baby out with the bathwater and jettisoning type systems entirely, just because Java's was lame.

I recall a training I attended in 2006 where the trainer said "friends don't let friends do Java." (Then PHP borrowed most of its OOP from Java 2, then stopped, while Java moved on.)

Crell,
@Crell@phpc.social avatar

@TheCodeLorax @ramsey And that's above and beyond the objections the Smalltalk folks had to C++ and Java as "that's not the OOP we meant, where the heck are you getting that half-assed concept?"

TheCodeLorax,

@Crell @ramsey If you were building a Green Field language, what would it look like?

Crell,
@Crell@phpc.social avatar

@TheCodeLorax @ramsey I've actually been trying to talk myself out of doing that, as I have thoughts. 🙂

Basic idea: No methods, just pipe operator. Type-based method overloading. Midori-style error handling (lightweight checked exceptions plus guards). Capabilities-based security. Algebraic effects-like handling of ambient functionality instead of manual DI.

At the moment that's about as far as I've gone. I need to actually try writing a parser/compiler for the ideas and see if they suck.

TheCodeLorax,

@Crell @ramsey Write what you want to work, then work backwards.

Crell,
@Crell@phpc.social avatar

@TheCodeLorax @ramsey Yeah, it's a matter of the time to do it.

Plus, my only really strong language is PHP, and PHP is... not the best language for building compilers. So I've been stalled on a lot of chicken and egg analysis paralysis.

I also want to build a new PHP framework, too, so... too many side projects.

TheCodeLorax,

@Crell @ramsey I just settle for building a |> operator in Swift. It’s really handy to be able to put code into functional patterns.

let result = input.someMethod()  
 |> firstTransform  
 |> secondTransform  
Crell,
@Crell@phpc.social avatar

@TheCodeLorax @ramsey Oh you don't need to sell me on a pipe operator, believe me. ;-)

TheCodeLorax,

@Crell @ramsey I don’t have to sell it. These babies sell themselves! 🤣

Crell,
@Crell@phpc.social avatar

@TheCodeLorax @ramsey I wish. PHP Internals is highly stubborn about almost any feature I am interested in. 😕

deleugpn,

@Crell

I wonder what it means when two fundamentally different people like you and me feel the exact same way about internals

@TheCodeLorax @ramsey

TheCodeLorax,

@Crell @ramsey That May be true, but it’s their job to be conservative. The feature creep of C# has been terrible and ruined the language (IMO).

Crell,
@Crell@phpc.social avatar

@TheCodeLorax @ramsey My big kick is that sometimes features A and B combine to give you feature C as a side effect. If you can do that, you don't need to design feature C. It just falls out naturally.

The more you can do that, the better the language because there's fewer moving parts to combine in interesting ways. But figuring out which A and B give you the most "free features" is a Hard Problem(tm).

TheCodeLorax,

@Crell @ramsey I believe that the number of features is less important than ensuring what Shouldn’t Work (tm). As code bases balloon, static analysis and compile-time error checking are more important than ever. Ensuring that the maximum amount of errors are caught at compile time is crucial.

Type checking and generics are a first level pass at this. Swift adds a great system to prevent null pointer errors. Rust has the borrower checker. Still, more are needed.

I have long argued that access controls are limited. Beyond the public/protected/private controls, the public interface needs to be divided up into Surfaces. For example, a typical adapter object has 2 surfaces. If one object accesses one surface, it should not access the other. That could be easily compile-time checked if it were annotated.

Crell,
@Crell@phpc.social avatar

@TheCodeLorax @ramsey Completely agreed about early-catch. I'm a big fan of "make the compiler do your job" / "make invalid states unrepresentable" / "if it compiled it must be right" philosophy.

I'm not sure what surfaces look like I practice, as you describe. Is there a language with that, or do you have an example?

TheCodeLorax,

@Crell @ramsey I’ve never seen a language implement a surfaces-like pattern. It is a hole in language design I’ve found over my many years.

TheCodeLorax,

@Crell @ramsey
The closest example I’ve seen is using extensions in most languages with scoping rules on the extension. It’s not ideal, but it functions.

Crell,
@Crell@phpc.social avatar

@TheCodeLorax @ramsey Can you throw together a pesudo code sample that a mere PHP dev might be able to follow? :-)

TheCodeLorax,

@Crell @ramsey
Don't sell yourself short. You're clearly a skilled developer if we can even have this discussion.

interface Adaptor {  
 // Generally available function  
 func doSomething();  
 surface FirstSide {  
 func getFoo() -> Foo;  
 }  
 surface SecondSide {  
 func getBar() -> Bar;  
 }  
}

class MyView: View consumes Adapter.FirstSide {  
 var adapter: Adapter; // Get from somewhere

 func legal() {  
 adapter.doSomething();  
 let a = adapter.getFoo();  
 }  
 func illegal() {  
 let b = adapter.getBar();  
 }  
}  

It adds two keywords consumes and surface to most languages. It's also additive and can be completely avoided if it isn't beneficial.

Crell,
@Crell@phpc.social avatar

@TheCodeLorax @ramsey Interesting. How would that be better than interface FirstSide extends Adapter, interface SecondSide extends Adapter?

TheCodeLorax,

@Crell @ramsey
In that particular example, it would be just fine to do that.Where this really becomes beneficial is in native UI development. In UIKit on iOS, the base UIView class has at least 3 surfaces as I would define them, and subclasses might even add more. There's a surface for the Layout Engine, another for the Renderer, and a last one for the Accessibility Engine.

The idea is that all these methods are public methods, but it generally is an error if your view code tries to access the rendering code for another view. When you're building a rendering engine, you may need to access these other surfaces of the object that should not generally be accessed.

TheCodeLorax,

@Crell @ramsey In my mind, it's really the reverse of an inherited interface. It goes to the idea of "prefer composition over inheritance." The object is composed of several smaller pieces. None of the pieces can be a complete object on its own, but each of the pieces provides a distinct set of functionality elements.

Crell,
@Crell@phpc.social avatar

@TheCodeLorax @ramsey So more interface Adapter extends FirstSide, SecondSide?

I guess I'm still not really groking how what you describe is different from "using narrow interfaces well."

TheCodeLorax,

@Crell @ramsey You’re on the right path, but missing it slightly.

interface First { … }  
interface Second { … }

class BaseClass { … }  
class MiddleClass: BaseClass implements First, Second { … }  
class ChildClass: MiddleClass { … }

class ConsumerClass {  
 func foo(middle: MiddleClass) { … }  
}

This is a very common situation where ConsumerClass requires an instance of MiddleClass. It may only be intended to access First on the object, but but it’s getting everything.

I’m using interfaces here for demonstration, but often there are no explicitly defined interfaces. It may just be a massive object with hundreds of methods. Even though there are no explicit interfaces, there are intended users for each group of methods.

Crell,
@Crell@phpc.social avatar

@TheCodeLorax @ramsey That still sounds like ConsumerClass::foo() should just be typing against First, and the God class you describe needs to be refactored. 🙂 (Or add a bunch of smaller interfaces.)

TheCodeLorax,

@Crell @ramsey Take a look at how many methods UIView has. I wish I could say it is bloated, but I’ve used all of them at one time or another. https://developer.apple.com/documentation/uikit/uiview

Crell,
@Crell@phpc.social avatar

@TheCodeLorax @ramsey I almost never do GUIs aside from web pages these days, so I'll take your word for it that's appropriate. But the point of "Well add a series of small interfaces then" is still valid, IMO.

TheCodeLorax,

@Crell @ramsey And that is exactly what this would be. It’s adding the ability to break large objects up into smaller interfaces and have those interfaces preserved through subclassing.

TheCodeLorax,

@Crell @ramsey Well said. I’m a big fan of Swift and it’s object/struct model, but even there I have to believe that it’s inheritance model would benefit from multiple inheritance.

MallardDuck,

@ramsey @Crell the part that makes me unsure about the concept is priorities.

Interfaces having methods will lead to methods that interact with properties. And I would prefer to see a solution that includes this fact.

Otherwise it leads to two routers IMO. The default methods reference properties the interface doesn't define but implementation need to. Or, default methods that do the thing which then call getXyzValue methods that classes implement to return their priority.

Crell,
@Crell@phpc.social avatar

@MallardDuck @ramsey The Property Hooks RFC (delayed until after the freeze, sadly) includes interface property declarations. The 2 RFCs weren't designed together, but would complement each other nicely.

heiglandreas,
@heiglandreas@phpc.social avatar

@ramsey @Crell Don't name it "interface" and I'll think about it...

Yes: Naming is hard. And important.

deleugpn,

@heiglandreas @ramsey @Crell what if the declaration is still an interface but when you implement it there is an opt-in syntax to whether you want the default code or you want only the contract? Something like

class Bar implements Foo with default
or
class Bar use Foo

Crell,
@Crell@phpc.social avatar

@deleugpn @heiglandreas @ramsey That wouldn't work with Levi's intended example use case: adding isEmpty to Countable, which would then become an error for anyone implementing Countable without "with default" (which is everyone). Opt-in wouldn't work.

heiglandreas,
@heiglandreas@phpc.social avatar

@Crell Apart from the logic mismatch that "countable" is able to tell whether something "isEmpty" and the BC breaking semantic versioning of adding a new function to an interface.... @deleugpn @ramsey

ramsey,
@ramsey@phpc.social avatar

@heiglandreas @Crell @deleugpn If the new function has a default implementation, how does it break BC?

heiglandreas,
@heiglandreas@phpc.social avatar

@ramsey There are IMO three possibilities:

a) the class already has a function that Implements something completely different
b) the class already has a function that uses a completely different signature
c) The class has no such function but suddenly the API of my class has changed without me actually doing anything

B is definitely a BC break, A might be and C for me kinda is as I might not want a new method in my function. Let alone one I'm not in charge of!

/cc @Crell @deleugpn

thomastospace,
@thomastospace@phpc.social avatar

@heiglandreas @ramsey @Crell @deleugpn Just so I, as an outsider, understand this correctly: This is a BC break for when packages implement methods in interfaces right?

As in, methods in interfaces are not a BC break in PHP itself?

heiglandreas,
@heiglandreas@phpc.social avatar

@thomastospace Adding a method to an interface (without a default implementation - which is currently not possible in PHP) is always a BC break.

All classes implementing that interface will break in their old implementation.

All classes need to uodate their implementation to match the new Interface-declaration.

/cc @ramsey @Crell @deleugpn

Crell,
@Crell@phpc.social avatar

@heiglandreas @thomastospace @ramsey @deleugpn Adding a method to a base class or trait has a BC break potential, yes, but it's orders of magnitude smaller than adding a method to an interface today.

You're objecting to reducing the BC break potential of interfaces to that of abstract classes.

Yes, both are non-zero breakage potential, but to ignore difference in scale is just ridiculous.

Literally every line of code has a non-zero breakage potential.

Crell,
@Crell@phpc.social avatar

@heiglandreas @ramsey @deleugpn A is not a BC break because the existing method will win anyway, because that's how inheritance works.

C is not a BC break. It's an API change, but not all API changes are BC breaks.

The only BC issue is B. Which is true. But it's a vastly smaller BC potential than now, where adding a method would break every Countable object in existence so they can... all implement the exact same one-line method.

The opt-in alternative is an IsEmptyable interface. Just, no.

mwop,
@mwop@phpc.social avatar

@heiglandreas @ramsey @Crell @deleugpn

I disagree.

A) and B) also occur without default impl, as the only time they kick in is if the method def is new. There's no change from status quo. The BC break exists even today.

C) only kicks in when updating to a new version of the interface that defines a method with default implementation. The benefit to the implementing class is that there is no immediate BC break. It only affects consumers of the implementation, as it provides a new method.

heiglandreas,
@heiglandreas@phpc.social avatar

@mwop The question was "How can adding a new method to an interface be a BC break, when there is a default implementation".

Yes. It's the same as without a default implementation.

The danger though is that people will stop thinking about that "because I can add a default implememtation" and introduce BC breaks in minor or even patch versions.

/cc @ramsey @Crell @deleugpn

mwop,
@mwop@phpc.social avatar

@mwop @heiglandreas @ramsey @Crell @deleugpn With a), there will be no issues at all if the signatures already match - the class definition takes precedence. With b), you'd have the BC break regardless - this happens already when signatures don't match. (I observed this updating to PSR-7 v2, as we'd omitted a void RTH in a method when updating, which caused PHP to barf. It's no different than if we'd defined a method that later got added to the spec.)

gmazzap,
@gmazzap@phpc.social avatar

@mwop @heiglandreas @ramsey @Crell @deleugpn there's also the case of intersection types. If I use an interface intersected with a type of mine, and that type has the method added to the interface, the intersection becomes invalid. Yes, we have that problem today. No matter default methods: if you add a method to an interface you'll always need a new major just like you do today. This RFC provides so little benefit against BC breaks that even mentioning it as a "pro" is a deceptive argument.

gmazzap,
@gmazzap@phpc.social avatar

@mwop @heiglandreas @ramsey @Crell @deleugpn as I commented in other threads, considering the "less BC breaks" argument is not valid, the only thing this RFC does is to implement multi-inheritance in a pretty obscure way, besides making abstract classes redundant. If multi-inheritance is desired, let's implement it explicitly and leave interfaces alone.

Crell, (edited )
@Crell@phpc.social avatar

@gmazzap @mwop @heiglandreas @ramsey @deleugpn As I noted on the list, interface default methods is the most common way that modern languages implement multiple inheritance. It's not obscure, it's conventional.

You may dislike the feature, but please do not misrepresent the facts.

heiglandreas,
@heiglandreas@phpc.social avatar

@Crell Fact is that a lot of other languages use default methods for multiple inheritance.

But fact is also that notnall of them use interfaces fir that.

The often cited example of Rust uses traits for that.

So if we want multiple inheritance, there are ways to do so, known from other languages, that do not involve interfaces.

And most importantly: This seems to be controversial. So perhaps lets not force that into a PHP version in the last minute.
@gmazzap @mwop @ramsey @deleugpn

heiglandreas,
@heiglandreas@phpc.social avatar

@Crell @gmazzap @mwop @ramsey @deleugpn There were multiple different ways sketched out how multiple inheritance can be implemented without interfaces.

Most of the time the counterargument was that that's not how this works in PHP.

If we want that feature we should discuss it and find the best way to implement it in PHP. And if that means changing how things work in the engine, then let's do it!

heiglandreas,
@heiglandreas@phpc.social avatar

@Crell @gmazzap @mwop @ramsey @deleugpn And if that means that we have to wait another year or two, then so be it. But we know that it will come in the best way and with solid technical and logical backing.

And for me that feature of allowing multiple inheritance would for sure be a reason to increment the major verion number.

heiglandreas,
@heiglandreas@phpc.social avatar

@Crell @gmazzap @mwop @ramsey @deleugpn To me whole discussion is like a customer wanting feature X, because they think it solves their problem A.

If the problem to be solved is multiple inheritance, then let's look at THAT and find a good way. How other languages solved that is an indicator of what might be possible. But as there are multiple different ways found, let's calmly think about options and pros and cons and how they solve problem A.

And yes, perhaps feature X IS the right thing...

Crell,
@Crell@phpc.social avatar

@heiglandreas @gmazzap @mwop @ramsey @deleugpn Rust traits are closer to PHP interfaces than to PHP traits, though it's not a direct comparison.

Yes, there are multiple ways it could be done. I said so on the list. My point is that calling default methods a weird, outlandish, or obscure approach is factually incorrect. It's the most common approach, especially among similarly structured languages.

heiglandreas,
@heiglandreas@phpc.social avatar

@Crell The RFC talks about solving BC breaks for new methods in interfaces.

Which to me is a technical solution for a non-technical problem (solid interface-definition and decent versioning)

The multiple inheritance is a great side-effect, but slipping in such a language changing feature en-passant seems too much for me.

So let's decouple the discussions about the interface default methods RFC and a - not yet proposed - muliple inheritance RFC

/cc @gmazzap @mwop @ramsey @deleugpn

heiglandreas,
@heiglandreas@phpc.social avatar

@Crell While Rusts traits might be closer to PHPs interfaces from the internal workings, they are not named interfaces.

For me this has nothing to do with the idea of multiple inheritance or default implementations but purely with the name. An interface as I understand it defines a concept but doesn't implement it.

/cc @gmazzap @mwop @ramsey @deleugpn

Crell,
@Crell@phpc.social avatar

@heiglandreas @gmazzap @mwop @ramsey @deleugpn

As I noted on the list, "as I understand it" needs to be a mutable concept. I didn't realize default interface methods were so common a week ago. Learning they were so common changed my mind on the whole idea.

Of the languages I reviewed that had an "interface" keyword, all but PHP and Typescript have default methods.

So as most similar languages understand it, interfaces can implement a concept.

deleugpn,

@Crell this is the core of the subject. As I stated on the list, nobody complained about the impact on php-src or the implementation implications. The only two reasons provided against it were either “too close to feature freeze” or “this is wrong” and the 2nd is just a matter of highly subjective opinion on the foundation of PHP as-is or old Java, maybe. This is all just depressing

@heiglandreas @gmazzap @mwop @ramsey

gmazzap,
@gmazzap@phpc.social avatar

@deleugpn I'm sorry, I never said it is wrong based on highly subjective opinion. This RFC does two things:

  • explicitly states of wanting to solve the BC break struggle and it's objective that goal isn't reached
  • introduce multi inheritance as a "side effect". Regardless everyone opinion on multi-inheritance I think is agreeable it shouldn't happen as a result of an apperently unrelated RFC, but deserves explicit RFC and maybe more explict implementation

@Crell @heiglandreas @mwop @ramsey

Crell,
@Crell@phpc.social avatar

@gmazzap @deleugpn @heiglandreas @mwop @ramsey

  1. It greatly reduces the BC breakage potential from guaranteed to maybe. I don't recall anyone saying it solved all BC issues.

  2. So would you support an identical RFC labeled "implement multiple inheritance the same way as most other languages"?

gmazzap,
@gmazzap@phpc.social avatar

@Crell

  1. reduces indeed. But understand if your lib is one of the few affected by breakage will be hard, even impossible without 100% coverage

  2. you insist a lot that other languages implement this way. But it easy to change the narrative. E.g. you say Rust Traits are like PHP interfaces with default methods, I say they are like PHP abstract classes with multiple inheritance. Why is your view more correct than mine?

@deleugpn @heiglandreas @mwop @ramsey

timwolla,
@timwolla@phpc.social avatar

@Crell @heiglandreas @gmazzap @mwop @ramsey @deleugpn

> Rust traits are closer to PHP interfaces than to PHP traits, though it's not a direct comparison.

And the "not a direct comparison" bit is a problem. As I mentioned on the list, Rust traits are effectively namespaced using the trait name and thus can't conflict.

Similarly Haskell typeclass functions are also namespaced, though not based on the typeclass name, but instead by their module name (https://stackoverflow.com/a/32573055).

deleugpn,

@Crell @heiglandreas @ramsey I see where you’re coming from with this but I’d still like the default even if it means not creating a better ability to introduce interface changes by default. I think php-src should start taking advantage of namespaces to make it easier to introduce BC breaks which would help with that

https://externals.io/message/120335#120354

gmazzap,
@gmazzap@phpc.social avatar

@heiglandreas @ramsey @Crell
> build up a class from separate pieces without having to manually write 50 proxy methods
Golang does it with type promotion, it's great. Atomic building of behavior is better implemented via composition, and type promotion is a form of that, this is multi inheritance, and sucks.

So IMO is not that it goes against what I think an interface should be, but the fact that it implements multi inheritance, and all the examples I've seen in the wild shoe that.

gmazzap,
@gmazzap@phpc.social avatar

@heiglandreas @ramsey @Crell If what you think is good for PHP is multi inheritance, enable it for classes and treat the Y problem in the same way this RFC does. Now it is explicit, interfaces stay the same, and everyone's happy. And don't tell me that this will not solve the problem of BC break when adding methods to interfaces, because this RFC does not do it either.

Crell,
@Crell@phpc.social avatar

@gmazzap @heiglandreas @ramsey So your answer is "do it like C++ and Python" rather than "do it like Java, Kotlin, Swift, Rust, Haskell, and Go."

Why go with what is clearly the minority approach?

gmazzap,
@gmazzap@phpc.social avatar

@Crell @heiglandreas @ramsey I like Golang here. Which does not have multiple inheritance, but composable objects via type promotion. Give me that please. Also, all the other langs that have default methods have also features like method overloading and type safety at complitation time that PHP does not have. Also, if the feature you want is multiple inheritance, why can't you just add it? Do you think is a good idea to change a fundamental trait of PHP becuase majority of other langs have it?

Crell,
@Crell@phpc.social avatar

@gmazzap @heiglandreas @ramsey I cannot speak for Levi's motivations. My impression is that the intent was what it said on the tin: "lots of interface methods have obvious default implementations, using a separate trait for that is needless extra work, let's just combine the two."

All of the "OMG multiple inheritance!" stuff came later, and wasn't the original goal.

Levi can correct me if I am misrepresenting him, of course.

gmazzap,
@gmazzap@phpc.social avatar

@Crell @heiglandreas @ramsey but merge two concepts without removing/deprecating one, means overlap and redundacy, hence confusion. If deprecating abstractclass is the ultimate goal that should be explicit, and could change the way some people look at it.

Crell,
@Crell@phpc.social avatar

@gmazzap @heiglandreas @ramsey I don't think Levi's goal is anything more or less than what the RFC says. Others no doubt have their own motivations, of whatever stance on the issue.

chrastecky,
@chrastecky@phpc.social avatar

@ramsey At this point, why not just support multiple inheritance? I find this dance around multiple inheritance weird and sad (in all those languages, not just php).

We already have traits which are multiple inheritance kinda-but-not-really.

And the diamond problem is clearly not a problem for traits. Not sure about default methods, didn't read the RFC in detail.

lewiscowles1986,
@lewiscowles1986@phpc.social avatar

A lot of blind assertions on the thread and in this message.

Good for the future of the PHP language.

How, for whom?

A lot of pointing at other languages and asserting something benefitted them.

Andreas Heigl comments were 💯 as was their shared code https://gist.github.com/brzuchal/89e9481bbd34a6ce3d95a68eabff038b

Code https://github.com/php/php-src/pull/11467/files Also makes for good reading

tests/classes/interface_method.phpt surely changes lots of folk cognitive model; which would be "fun" for reviews.

Save for PHP9. Messes with UML diags

ramsey,
@ramsey@phpc.social avatar

It looks like at least one person flipped their vote, so the vote is an even 50/50, now. It needs a 2/3 majority to pass, so if 5 more folks flip their votes, it’ll be passing.

Skoop,
@Skoop@phpc.social avatar

@ramsey I haven't read the original RFC, but do I understand correctly that the proposal is to allow defining not just method blueprints in an interface but also (abstract) methods?

If so, I have had several situations where I wished this was possible and would love for that to be possible :)

stof,

@Skoop Interfaces are already able to define abstract methods (that's all they can define for now) /cc @ramsey

heiglandreas,
@heiglandreas@phpc.social avatar

@Skoop @ramsey the blueprint is already abstract methods.

It's about actual implementations. Which is an interestimg idea.

but please not under the name "interface"

mauriciofauth,
@mauriciofauth@phpc.social avatar

@ramsey I changed my vote to yes.
What changed my mind was something @Crell said:
"Traits are a surgical tool with a narrow set of uses, and systems that over-use them cause problems."

The same can be said for default methods. Like traits, there are some specific use cases for them, but if you are using them too much, you are doing something wrong.

There is one thing I still disagree with the RFC. Using default methods to avoid a BC break when adding methods to an interface is a terrible idea.

gmazzap,
@gmazzap@phpc.social avatar

@mauriciofauth @ramsey @Crell The problem is that BC break is still there. Speaking semver, if you a method to an interface, even if you provide a default, you still need to release a new major. So the only theoric good use of default methods, that is prevent BC breaks, is not achieved, thus this RFC does nothing good and a lot of harm. Just to name one thing, the implications with intersection types is huge.

Crell,
@Crell@phpc.social avatar

@gmazzap @mauriciofauth @ramsey What does it have to do with intersection types?

gmazzap,
@gmazzap@phpc.social avatar

@Crell @mauriciofauth @ramsey I have a function that accepts an intersection of a class of mine + a framework interface. The framework adds a default method to that interface, and that method exists in my class with a different signature. The intersection type is now invalid. So the framework break my code, even if my code did not change.
It means this RFC which aims at solving BC breaks introduces new way of creating breaking changes.

Crell,
@Crell@phpc.social avatar

@gmazzap @mauriciofauth @ramsey That's no different than if the interface added a method with a different signature than yours and didn't provide a default. That's still going to break in subtle and annoying ways.

gmazzap,
@gmazzap@phpc.social avatar

@Crell @mauriciofauth @ramsey Exactly. What you are saying is: the problem we want to solve is prevent BC breaks with interfaces. This RFC does not solve the problem, but we want it anyway.

Crell,
@Crell@phpc.social avatar

@gmazzap @mauriciofauth @ramsey No, the RFC adds certain functionality, and improves BC in one way, but not in this other way that's more or less the same either way.

It's not perfect, but still a net win.

gmazzap,
@gmazzap@phpc.social avatar

@Crell @mauriciofauth @ramsey when I criticized this RFC people answered me that it would be a way to overcome BC breaks, so that is where I'm coming from. Now it seems the selling point is "the correct way" to implement multi-inheritance. Can I ask why is it better than implementing multi inheritance explicitly? As in allow extend to be followed by multiple classes? Isn't that the same feature but less impactful, more explicit, and more "natural" for PHP?

Crell,
@Crell@phpc.social avatar

@gmazzap @mauriciofauth @ramsey From a completely abstract place:

class A extends B, C {}

and

class A implements B, C {}

would be effectively equivalent in power. The difference is squishy feel stuff.

Classes "are supposed to" have impl, interfaces "are supposed to" have type defs. If done via interfaces, no default method becomes the mental default.

Honestly, for me the strongest arg is "via interfaces is more common," so it's easier for ppl coming to the language.

Crell,
@Crell@phpc.social avatar

@gmazzap @mauriciofauth @ramsey At some point in this, effectively, interfaces and abstract classes merge into one concept. What particular syntax that uses is subjective.

I couldn't say why the market has gone with interface-based syntax in most cases without a lot more research, but that seems to be what's happened.

deleugpn,

@Crell @gmazzap @mauriciofauth @ramsey

I would take a shot at guessing it’s the mental model. Defining an abstract class is about thinking of an object behavior with multiple levels of inheritance and where that object fit in the hierarchy. Defining interfaces is just about an expected contract where multiple levels of hierarchy are barely relevant. I guess there’s nothing really technical about it, just how the idea embedded in people’s minds.

gmazzap,
@gmazzap@phpc.social avatar

@deleugpn If we stop talking about syntax differences, and focus on concepts, we find out many languages have a construct that is
both abstraction and partial implementation. And guess what, PHP has it as well, it's abstract class. But other languages allow multiple inheritance from that construct, PHP doesn't. Support multiple names after extend and PHP becomes inline with other langs.

@Crell @mauriciofauth @ramsey

gmazzap,
@gmazzap@phpc.social avatar

@deleugpn It's true that many other langs do not have an abstraction-only construct, like PHP interfaces, but if you add implementation to interfaces, you make abstract classes redundant. And because I doubt you'll ever be able to deprecate abstract classes, than I think we should avoid create a duplicate feature in the language. Especially when we can obtain the same thing other language have by allowing multiple inheritance on abstract classes.

@Crell @mauriciofauth @ramsey

Crell,
@Crell@phpc.social avatar

@gmazzap @deleugpn @mauriciofauth @ramsey

Java, Kotlin, and C# all have abstract classes, and interface default methods. Only Swift does not.

I agree it seems redundant, but other languages seem to still keep both.

Also, multiple-inheritance via extends would make interfaces redundant just as much. The conclusion I draw is that the line between the two is much more murky than the Java 2 mindset (still prevalent in PHP) would have us believe.

mauriciofauth,
@mauriciofauth@phpc.social avatar

@Crell @gmazzap @deleugpn @ramsey
Why we can't have multiple inheritance via extends and interface default methods?

Crell,
@Crell@phpc.social avatar

@mauriciofauth @gmazzap @deleugpn @ramsey I'm sure it's technically feasible, but for anyone who thinks even just one of them would be confusing, that would be multiplicatively more confusing. 🙂

gmazzap,
@gmazzap@phpc.social avatar

@mauriciofauth

So same feature, implemented in two different ways?

@Crell @deleugpn @ramsey

gmazzap,
@gmazzap@phpc.social avatar

@Crell

> Java, Kotlin, and C# all have abstract classes, and interface default methods

See? 3 languages, not 13.

> multiple-inheritance via extends would make interfaces redundant just as much

Conceptually, an interface would be abstraction-only. So you could have have an abstract class with abstract methods only, but still conceptually not a duplication.

@deleugpn @mauriciofauth @ramsey

deleugpn,

@gmazzap if you were designing a language in 2023 would you still make that concept part of the syntax instead of providing abstract classes only and letting users decide whether they want abstract-only or abstract+body?

@Crell @mauriciofauth @ramsey

Crell,
@Crell@phpc.social avatar

@deleugpn @gmazzap @mauriciofauth @ramsey I'd probably go all the way to type classes like Rust and Haskell, which eliminates the distinction in the first place. And effectively gives you an equivalent of default methods through other mechanisms.

gmazzap,
@gmazzap@phpc.social avatar

@Crell That's where I disagree. Languages that do not have the distinction can be seen both as having interfaces with default methods or abstract classes with multiple inheritance. But PHP has the distinction, we should just ignore it in my opinion.

@deleugpn @mauriciofauth @ramsey

gmazzap,
@gmazzap@phpc.social avatar

@Crell @mauriciofauth @ramsey I still don't agree that make a PHP-specific thing (abstract class), and mess with concept PHP devs have learned for decades, is a good thing because of "market reason" when the same feature can be obtained in an idiomatic way.

gmazzap,
@gmazzap@phpc.social avatar

@Crell @mauriciofauth @ramsey Most other languages don't have abstract classes separates from interfaces. PHP has that. That is a trait that is idiomatic to PHP, so weshould aim to keep it. If interfaces have default methods, abstract classes become redundant.

Crell,
@Crell@phpc.social avatar

@gmazzap @mauriciofauth @ramsey Abstract classes are already redundant and have been for many years:

https://www.garfieldtech.com/blog/beyond-abstract

And abstract classes can be found in C++, Java, Kotlin, C#, Python, and TypeScript. Of them, Java, Kotlin, and C# also have interface default methods. Python doesn't differentiate between a base class and an interface to begin with, so you could spin it either direction.

If we were designing PHP greenfield, I'm sure we'd do it differently. But we're not.

deleugpn,

@mauriciofauth @ramsey @Crell thank you!! 🙏

tomasveneny,

@ramsey @Crell

The beauty of PHP was / is in simple tool for programming where you can use what you need / want.

It looks to me like some RFC wants to make PHP step-by-step language.

I also noticed that there is a trend for using another language like a argument for RFC.

ramsey,
@ramsey@phpc.social avatar

@tomasveneny @Crell I think this RFC continues the trend of allowing users to use what they want / need. It doesn’t prescribe a specific step-by-step way of doing things.

Crell,
@Crell@phpc.social avatar

@ramsey @tomasveneny I don't know what "step by step language" means. Basically all languages are trending toward more formality and static information, because those are unequivocally good things.

Referencing other languages is good for context. It's not itself an argument for/against any feature, just context to know "does this work out well, or did everyone try to rip it back out later?"

tomasveneny,

@Crell @ramsey

I mean with "step by step language" that you need to tell programmers literately what it has to do.

I will say it this way - when I was kid I had (and still have) Lego and you could build anything you want - limited by your imagination and pieces.

Today Lego is different - you can build only what Lego prepares for you.

I see this RFC same way.

ramsey,
@ramsey@phpc.social avatar

@tomasveneny @Crell > you can build only what Lego prepares for you.

What? I can go to the nearby Lego store and buy a box of assorted Lego pieces and build anything I want. You don’t have to buy the sets.

Crell,
@Crell@phpc.social avatar

@ramsey @tomasveneny Yeah, that is a total non-sequitor. And nothing to do with this RFC, as it doesn't dictate that you must do anything.

tomasveneny,
tomasveneny,

@ramsey @Crell

I can buy only the sets where I live.

However it looks to me like you missed the point of it.

ramsey,
@ramsey@phpc.social avatar

@tomasveneny @Crell Sorry. You used an analogy that doesn’t really work, so I got hung up on that. 🙂

ramsey,
@ramsey@phpc.social avatar

@tomasveneny @Crell Lego’s design patent also expired years ago, and they almost went bankrupt. Focusing on partnerships with movie and TV properties (especially through video game licenses) is what saved them. Since other companies can now produce blocks that connect with Lego brand block, Lego have decided to focus on sets to be competitive.

Crell,
@Crell@phpc.social avatar

@ramsey @tomasveneny For instance, "multiple inheritance" has a bad rap, but by my count, C++ and Python have it natively, and Java, Kotlin, Swift, and Rust all have it via interface default methods (or their language-equivalent). So... maybe it's not as bad as people think.

Crell,
@Crell@phpc.social avatar

@ramsey @tomasveneny This made me curious, so I did some research. I posted my results to the mailing list thread.

Short version: Implementing multiple inheritance is super common, and doing so via interface default methods is the most common way it's done. PHP is weird by not having multiple inheritance by now.

I did not expect that.

tomasveneny,

@Crell @ramsey

Maybe being weird is sometime better than being mainstream

  • All
  • Subscribed
  • Moderated
  • Favorites
  • php
  • Durango
  • DreamBathrooms
  • khanakhh
  • GTA5RPClips
  • osvaldo12
  • magazineikmin
  • mdbf
  • InstantRegret
  • rosin
  • Youngstown
  • slotface
  • everett
  • kavyap
  • ngwrru68w68
  • megavids
  • modclub
  • tester
  • tacticalgear
  • cubers
  • thenastyranch
  • cisconetworking
  • ethstaker
  • Leos
  • provamag3
  • normalnudes
  • anitta
  • JUstTest
  • lostlight
  • All magazines