Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the best pattern for passing Immutable Collections across APIs

Before immutability, IEnumerable was the go-to interface in many APIs since this had the advantage that the API was insensitive to the actual type of the passed object.

public void DoSomeEnumerationsWithACollection(IEnumerable<Thing> collectionOfThings)
{ 
   foreach (var thing in collectionOfThings) doSomethingWith(thing);
   foreach (var thing in collectionOfThings) doSomethingElseWith(thing);
}

Of course there are at least two downsides to this:

  1. The code behind the API can't rely on the immutability of collectionOfThings and may encounter a "collection modified" exception or hit other other subtle issues.

  2. We don't know whether collectionOfThings is real collection or simply a deferred query. If we assume it's a real collection and it isn't we run the risk of degrading performance by running multiple enumerations. If we assume it's a deferred query and it's actually a real collection then turning it into a local list or other frozen collection incurs unnecessary cost although it does help protect us against the first problem (there's still a race condition whilst performing the "ToList" operation). Obviously we can write a small amount of code to check for this and try to do the "right thing" but this is annoying extra clutter.

I must admit I have never found a satisfactory pattern to address this other than using naming conventions. The pragmatic approach seemed to be that IEnumerable was the lowest friction approach for passing around collections, despite the downsides.

Now, with immutable collections, the situation is much improved...

public void DoSomeEnumerationsWithACollection(ImmutableList<Thing> collectionOfThings)
{ 

There is no longer a risk of collection modification and there's no ambiguity about the performance impact of multiple enumerations.

However, we have apparently lost flexibility on the API since we now have to pass in an ImmutableList. If our client had some other kind of enumerable immutable collection, it would have to be copied into an ImmutableList in order to be consumed even though all we want to do is enumerate it.

Ideally we'd be able to use an interface like

public void DoSomeEnumerationsWithACollection(IImmutableEnumerable<Thing> collectionOfThings)

but of course, an interface can't enforce semantics like immutability except by convention.

Using a base class might work

public void DoSomeEnumerationsWithACollection(ImmutableEnumerableBase<Thing> collectionOfThings)

except that it's considered bad form to create unsealed immutable classes lest a subclass introduce mutability. And in any case, this hasn't been done in the BCL.

Or we could just keep using IEnumerable in the API and using a naming convention to make it clear our code relies upon an immutable collection to be passed in.

So... my question is what patterns are considered best when passing around immutable collections? Is ImmutableList the "new IEnumerable" once we start using immutablity or is there a better way?

Update

IReadOnlyCollection (suggested by Yuval Itzchakov below ) is a distinct improvement over IEnumerable but still does not fully protect the consumer against uncontrolled changes in the collection. It's notable that the Roslyn codebase makes heavy use of immutability (mainly via ImmutableArray) and appears to use explicit typing when passing these into other methods though there are a couple of locations where ImmutableLists are passed into methods that accept IEnumerable.

like image 272
NeilMacMullen Avatar asked May 16 '15 10:05

NeilMacMullen


People also ask

How do you make a collection immutable?

In Java 8 and earlier versions, we can use collection class utility methods like unmodifiableXXX to create immutable collection objects. If we need to create an immutable list then use the Collections. unmodifiableList() method.

What is the advantage of having immutable collections and when we have to use the immutable collections?

Less heap space: The space required to store a collection data is very less as compared with the traditional approach in earlier versions of java. Faster access to data: As the overhead to store data and wrap into Collections. unmodifiable is reduced, now data access becomes faster.

Which of the following collection is immutable?

The datatypes like int, float, bool, str, tuple, and Unicode are immutable. Datatypes like list, set, dict are mutable.

Which is a collection of objects which ordered and immutable?

A set is an ordered collection of objects.


1 Answers

After a few years of using immutable collections heavily, I have settled on the convention of using ImmutableArray almost everywhere. This doesn't address the original desire to allow flexibility in the API but in practice it's rare that I use ImmutableList or another list-like structure and when I do, it's usually not much overhead to call ToImmutableArray to get the data across the interface.

like image 65
NeilMacMullen Avatar answered Oct 11 '22 18:10

NeilMacMullen