People like it easy
I don't see this pattern widespread in C# though, even with the benefits of immutability
It's not a technical consideration, it's a human one. Even forgetting about development, people will generally prefer having a panacea instead of individually specialized remedies, simply because it's easier to always use the same thing than to have to remember what to use when. The "duct tape and WD-40 is all you ever need" joke is a good example of that.
At the most basic level, you'll see usage of List<>
because it's the closest to a panacea you're going to get: full CRUD operations on the same object. It's the "duct tape and WD-40" of the data collection world.
And for a trivial (I cannot stress this enough) project, that's fine enough. Small projects don't have the kind of complexity where you're going to not know where/if a collection is being modified. I'm talking about half-hour projects where I slap together a console app to automate a short job - nothing professional or with an expected long lifetime.
How many different types/interfaces of collections a developer wants to use very much depends on how much that developer cares to distinguish between them. I'm not arguing where you should draw the line; I'm just addressing your observation why using more specialized collection types isn't as prevalent as you expect it to be.
Basic immutability often suffices
While I'm aware that it's not going to cover everything you want it to, using an IEnumerable<T>
(which is usually the next step up from just using a List<>
) mostly gets you towards a reasonable expectation of immutability.
It's not impossible for your caller to figure out that the IEnumerable<>
you passed is actually a List<>
and thus making it possible for them to cast it back and start modifying the collection. You're going to want to avoid that, which means having to resort to collection types with a stronger enforcement of immutability.
However, first you have to ask yourself whether casting the IEnumerable<>
is a significant concern. Is this really exposing a weakness?
- Sometimes the answer will be yes (e.g. if that same list object is being used in other locations, if the caller cannot be trusted),
- Sometimes the answer will be no (e.g. if this list object was generated specifically for this caller and does not get reused, if the caller can be trusted).
Cost vs benefit
If the answer to the above question is no, you might not want to bother evaluating for other collection types, investing more time in this is not going to add significant value.
If the answer to the above question is yes, they you can start considering enforcing read-only collection types such as IReadOnlyList<>
or IReadOnlyCollection<>
. This allows you to reuse the same collection object without worrying that others will start modifying it.
I'm wondering whether it would be wrong for me to transition to function signatures like this in C#, and if there's a reason this isn't more widespread?
This is a cost-benefit analysis. Just because A is better than B does not mean that it's worth spending the extra time it takes to use A instead of B.
You seem to highly value immutability, but there are plenty of developers who don't care about it to the same degree as you do, and they're going to come up with a very different cost-benefit analysis than you.
Note that this depends on the scope and expectation of the project. If you're setting up an enterprise architecture, you're going to be more inclined to immediately enforce immutability from the get go to prevent issues when the codebase inevitably grows beyond something that one person can keep track of; but for a small personal project there's no point to spending the effort policing something that's likely to cause less issues than the effort of policing it.
To summarize
Feel free to take that route, there's nothing wrong with doing so. I would advise you to read up on existing collection types as there'll likely already be a type that fits your specific needs.
But you asked why it's not already in use by other .NET developers, and that's what this answer addresses. To summarize:
- Not every developer cares about immutability
- The poor man's immutability (
IEnumerable<>
) often suffices
- The effort of implementing immutability is not always worth it - but that is both highly contextual and highly subjective.
- Blanket immutability isn't always the best (or practical) way to go about something
As an aside...
but after studying F# and functional programming I've seen the benefits of immutability. Now, I'm thinking of getting rid of mutability entirely in C#
This sounds like me. I am very susceptible to being eager to use something that I've just learned about. I go through that same cycle every time I learn a new methodology or library.
But I do have to admit that in a majority of those cases, after rigorously implementing this new approach (and possibly evangelizing it a tad too much), I start realizing that it isn't always worth the effort or that "it may be pretty, but it ain't practical".
I'm not trying to discourage you, immutability definitely has its perks and there's value to implementing it, and you should definitely always try to improve your coding skills by trying out new things.
I'm just trying to point out that the eagerness to use a tool doesn't always mean it should be used as much as you currently want to use it. It's rare for something to be both elegant in theory and practical in reality, and (sadly) practicality tends to win out when it's one or the other.
IEnuerable<T>
is an immutable sequence. Where performance isn't a concern, stuff likething.Select(f).OrderBy(g).ToList()
is quick to type and hard to get wrong.