If you voted against the Interface Default Methods RFC for #PHP, 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.

[RFC] [Discussion] OPcache Static Cache - Externals

#externals - Opening PHP's #internals to the outside

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.

@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 :)

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

@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"

@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.

@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.
@gmazzap @mauriciofauth @ramsey What does it have to do with intersection types?
@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.
@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.
@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.

@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.

@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?

@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.

@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.

@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.

@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.

@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

@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

@deleugpn If there's support for multiple inheritance on abstract classes, than if you wish to have what other languages have, you can forget about interfaces and only use abstract classes. Interfaces will be there with a reason to exists for example for those poeple that do not want to use inheritance.

@Crell @mauriciofauth @ramsey

@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.

@Crell @gmazzap @deleugpn @ramsey
Why we can't have multiple inheritance via extends and interface default methods?
@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. 🙂

@mauriciofauth

So same feature, implemented in two different ways?

@Crell @deleugpn @ramsey

@gmazzap that’s what most languages designed in the 90’a seem to have. There’s already stigma around multi inheritance and it has the potential to create a messy structural hierarchy, which is the primary reason I don’t care much for it. The discussion seems to have lodged itself in a technicality that more or less revolves around “hey look this is mutiple inheritance” when the fact is we barely care for it.

@mauriciofauth @Crell @ramsey

@gmazzap what we want is potential reduction of BC breaks when modifying a contract and also the ability to provide implementation for contracts that barely need to deviate from a single implementation. Whether folks will use it to make multiple inheritance is like arguing that if conditions are bad because it allows too much flexibility to make the codebase a mess. You can’t have a modern language without “if”

@mauriciofauth @Crell @ramsey

@deleugpn
The potential resolution is a problem for me as well.
As others have said, this introduces the risk of people releasing changes to interfaces withot calling for the BC break because they have added default methods.
Also, if you are one of the few people affected by the BC break, telling that will be hard, unless you have 100% coverage.

@mauriciofauth @Crell @ramsey

@deleugpn

I'm telling to build multi inheritance via abstract class, so no stigma from me.

But the thing is this that even if you don't care, that is a fundamental change in the language, and should not happen by accident, IMO.

@mauriciofauth @Crell @ramsey

@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

@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

@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.

@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

@deleugpn
I would take the Golang path, and prevent any form of interitance, facilitate composition, and have structural typing.

But, editing a language and building a language from scratch are two different things. Abstract classes are there. Ignoring it, and re-implement them via interfaces does not sound like a great idea to me.

@Crell @mauriciofauth @ramsey

@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.

@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.

Beyond Abstract classes | GarfieldTech

@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.

@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?

@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.

@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 #PHP 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.)

@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?"
@Crell @ramsey If you were building a Green Field language, what would it look like?

@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.

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

@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.

@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
```

@TheCodeLorax @ramsey Oh you don't need to sell me on a pipe operator, believe me. ;-)
@Crell @ramsey I don’t have to sell it. These babies sell themselves! 🤣
@TheCodeLorax @ramsey I wish. PHP Internals is highly stubborn about almost any feature I am interested in. 😕
@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).

@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).

@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.

@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?

@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.
@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.
@TheCodeLorax @ramsey Can you throw together a pesudo code sample that a mere PHP dev might be able to follow? :-)

@Crell

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

@TheCodeLorax @ramsey

@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.

@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.

@chrastecky @ramsey the problem with traits is that you just can't use them as contracts in type system. All you can do is to use pair of trait+interface. Don't you think that interface with default implementations solves that 🙂?
@codito @ramsey From practical point of view, yes. But IMO it's not a clean solution. Just allow extending multiple classes and be done with it.