In favor of Immutable interfaces

24 views
Skip to first unread message

Olivier Cailloux

unread,
Jul 9, 2024, 10:33:48 AM (13 days ago) Jul 9
to guava-...@googlegroups.com
Dear Guava Team,

I’d like to argue for letting the guava ImmutableCollection types (ImmutableList, ImmutableGraph, and so on) implement new interfaces, say, ImmList, ImmGraph, and so on, that would reflect their contracts but allow subclassing. I realize that this has been evoked already but have not found detailed discussions about this specific proposal and its strengths (and weaknesses), so I have some hope that this proposal deserves to be argued for.

The proposal

In more details, there would be one Java interface for each Immutable* type. The interface would exhibit the contract of its corresponding Immutable type. For example, there would be an ImmCollection interface (or any other name considered adequate) that would extend Collection and whose instance methods signature and javadoc would be identical to ImmutableCollection, an ImmList interface that would extend ImmCollection and List with instance methods and javadoc identical to the ImmutableList type, and so on. The existing Immutable* types would be declared to implement those interfaces.

Some counter-arguments

Let me start with the usual counter-arguments that have probably sprung to the mind of the reader. It is usually said, correctly of course, that the fact that the Immutable* types are classes and not interfaces make them non implementable by anyone else than the Guava team. This is sometimes considered a feature, not a problem, as it may help the user trust that the implementations are indeed immutable. Also, it is observed that the Immutable* types actually correspond to several implementations, not just one, and that it is hard to see a reason for someone to want one’s own implementation for at least some of these types. Finally, interfaces also sometimes (when sealed) do not permit own subtyping, so the interdiction of “own subtyping”  is not specific to classes. As a result, the Guava team argues that the Immutable* types should effectively be treated as interfaces, not as classes. (Refs: Colin DLouis WassermanWikiChris Povirk.)

Argument in favor

I do not think that these arguments stand up to scrutiny when considering the specific current proposal. It is true that forbidding anyone else than the Guava team to implement Immutable* types may be trust-building, but isn’t it a bit lacking modesty (no offense intended) about the Guava team? If one agrees with the general advice to API builders to return interfaces rather than types in order to be free from swapping implementation in the future, it also seems reasonable to admit that for Immutable* types, the Guava team could not have come up with the best possible implementations for every possible use cases. Someone could face a very specific situation where a very specific implementation will feature better performances (hints of such situations herehere). Also, having interfaces may enable use cases that are extremely difficult to achieve without first convincing the Guava team to change their implementation (see here). In short: decentralization of decisions, by letting developers provide their own implementations, is usually considered in the object oriented world as outweighting the benefit of having just one organization controlling the implementation of a type, even though it is true that the one-organization-implementing approach gives more guarantees of correctness to the users who trust that organization.

Most importantly, the current proposal still provides the possibility for anyone to use Immutable* types if they want the guarantee of Guava-implementation of the types; it just _adds_ the possibility for an API developer to be not tied to the Guava-implementations for those who value the freedom of swapping implementation.

Let me make it clear that I do _not_ think that there is any reason to mistrust the quality of the implementation of the Guava team: it is clear that this library is of extremely high quality. But still, as a matter of principle and as a practical matter in specific cases, it seems to me to be valuable to let the users of Guava (at least the ones who provide API that they intend to maintain for some time) apply the conventional wisdom of giving their future selves an ability to come up with their own (or any other organisation than Guava) implementation of the types that they commit their API to return.

I hope that this proposal will be given some thoughts and I apologize if it is redundant.

Chris Povirk

unread,
Jul 9, 2024, 4:41:39 PM (13 days ago) Jul 9
to Olivier Cailloux, guava-...@googlegroups.com
I can't say I recall any past discussion of additionally providing interfaces along with the existing types.

There are for sure cases in which an alternative implementation would work better. For example, someone just today asked us about EnumSet/EnumMap-backed ImmutableMultimap implementations :)

My initial reaction, right or wrong, is that one effect we'd see inside Google in practice is that people would start mocking the types. And then any user of an ImmList who is debugging a problem may have to look with suspicion on basic operations like iteration.

That is, to be fair, already what can happen if you use plain List in your APIs. And people don't usually mock List, thankfully. And when they do, it's still not the end of the world. Plus, inside Google3, one of the first things we'd do is ban mocking of ImmList, eliminating this problem... for us :)

Still, the same kinds of problems can happen with other implementations. Yes, maybe we're being immodest, but ImmutableList and friends have undergone years of production use, and they're tested beyond what would be reasonable for most custom implementations. I don't think the difference is between "perfect" and "broken," but if ImmList were widely adopted, then I'd expect the difference between "extremely solid" and "very solid" to add up over time, and it may outweigh the gains that the interfaces could sometimes unlock.

If I were to engage in some wishcasting, I'd hope that the use cases for special immutable-collection types would be relatively localized. In such a case, a project could define its own ImmList class, with separate implementations for BasicImmList (backed by Guava?) and ConcatenatedImmList, etc.

Alternatively, if immutable-collection instances need to be passed around throughout a system, then maybe the situation is one in which Eclipse Collections or another alternative would be more suitable, since other libraries have been used in production and tested for difference use cases than Guava.

j...@durchholz.org

unread,
Jul 10, 2024, 2:22:10 AM (12 days ago) Jul 10
to guava-...@googlegroups.com
Just a remark from the sideline:

On 09.07.24 22:41, 'Chris Povirk' via guava-discuss wrote:
> That is, to be fair, already what can happen if you use plain List in
> your APIs. And people don't /usually/ mock List, thankfully. And when
> they do, it's still not the end of the world. Plus, inside Google3, one
> of the first things we'd do is ban mocking of ImmList, eliminating this
> problem... for us :)

If somebody starts mocking basic data structures, they own any problems
they create, and I believe most also know this and won't come asking for
help.
I also suspect that if people don't mock List, they will likely not mock
ImmList either, for the same reasons, whatever these are.

On adding interfaces in general:
They can be a *huge* improvement if done with a clear strategy in mind.
I have seen that done once, with the EiffelBase library, and it was a blast.
Done in an ad-hoc fashion, it's a very easy way to paint yourself into a
corner.

Regards,
Jo

Chris Povirk

unread,
Jul 10, 2024, 12:41:42 PM (12 days ago) Jul 10
to j...@durchholz.org, guava-...@googlegroups.com
If somebody starts mocking basic data structures, they own any problems
they create, and I believe most also know this and won't come asking for
help.
I also suspect that if people don't mock List, they will likely not mock
ImmList either, for the same reasons, whatever these are.

I am probably projecting too much of our own experiences onto the wider Java community: We are used to having our changes blocked because some team decided to mock a List to expect some part of Guava to call isEmpty() but not for it to call size(), leading to breakages when we change that part of Guava to call size() instead of isEmpty(). We also hear from other teams in similar situations. So we end up in a situation in which Project A mocks a type owned by Project B, leading to failures after a change by Project C. We have largely improved this by cracking down on bad mocking Google-wide. (And I don't mean to suggest that a lot of people had such mocks, but if even a very small fraction do, then changes like size()->isEmpty() break someone, and we need to fix the failing tests or at least verify that those tests don't indicate real problems, just bad tests.)

All that said, I've been over-focusing on mocking because of the problems in earlier years, which weren't always that bad but which always felt so unnecessary :)
 
On adding interfaces in general:
They can be a *huge* improvement if done with a clear strategy in mind.
I have seen that done once, with the EiffelBase library, and it was a blast.
Done in an ad-hoc fashion, it's a very easy way to paint yourself into a
corner.

Yes, this is probably closer to the main answer: We'd want to do new interfaces right, so we'd want a full understanding of the design space. That would mean a lot of investigation of Guava (e.g., should the interfaces live in common.base so that Splitter can use them?) and our users, hopefully both inside and outside Google. But we've increasingly shifted our efforts toward supporting platforms (e.g., Kotlin, Android with Java 8 support) rather than toward development of large, new libraries. That may or may not be the right decision, but it means that we have a backlog of cool ideas that would need time to investigate (example). And my guess is that ImmList and friends, while not requiring as complex a design as some other candidates, aren't going to make the top of the list anytime soon :(
Reply all
Reply to author
Forward
0 new messages