4

I realize this is a subjective question, 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# unless specifically required. In essence, where before I might have written a function with a signature like -

public static int F(List<string> input)

I'm now paranoid if that function might mutate the list unless I want it to. So now I'm thinking of doing something like

public static int F(ImmutableList<string> input)

Also, if I actually do want to mutate a list, before I'd do something like

public static void F(List<string> input)

and mutate it directly, but now I'm thinking of doing something like this instead:

public static ImmutableList<string> F(ImmutableList<string> input)

and just re-assigning the new list.

I don't see this pattern widespread in C# though, even with the benefits of immutability. 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?

7
  • Look at the rust language. Immutable is the default. Commented May 23, 2020 at 9:14
  • 2
    This pattern is abundant anywhere you find LINQ: an IEnuerable<T> is an immutable sequence. Where performance isn't a concern, stuff like thing.Select(f).OrderBy(g).ToList() is quick to type and hard to get wrong. Commented May 23, 2020 at 11:02
  • "I've seen the benefits of immutability" - what were those benefits exactly, and how did you measure them?
    – Steve
    Commented May 23, 2020 at 11:37
  • @Steve Let's say I delegate an interface for someone else to implement that accepts a list. If that's immutable, I don't need to worry that the list might be mutated through some obscure bug. That allows me to think on a more abstract level about things. I think the ability to specifically say when I want something to mutate with the default being immutability is ideal.
    – user4779
    Commented May 23, 2020 at 13:47
  • I doubt this has relieved you of worries or elevated your thinking. It appears to have created significant attention and worries where none likely existed before - you say so yourself. It's standard programming practice nowadays not to assign to or modify method parameters passed in (unless there is intent to mutate). The primary purpose for which ImmutableList was designed, is for asynchronous programming - which also does not relieve any burdens, but adds to them greatly - where the risk was that the caller would modify the list under the feet of the callee... (1/2)
    – Steve
    Commented May 23, 2020 at 14:58

2 Answers 2

8

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.

1
  • So if language has immutability by default, like Scala or Rust, developers wouldn't "have to care enough" to use immutable types and that's a problem solved!
    – Xolve
    Commented May 31, 2020 at 19:58
6

Its actually a fairly common pattern. eg.

public static string Concat (ReadOnlySpan<char> str0, ReadOnlySpan<char> str1, ReadOnlySpan<char> str2);

I guess the reason you don't see a lot of this in peoples code, is that mutability is super useful. Especially in lists, where you want to add and remove things all the time.

Not everyone has drunk the functional cool aid.

3
  • Could you guide me on where I can find the happy medium between OOP and FP? I honestly see the strengths of both. Having interfaces, classes with single responsibilities etc, but combined with immutability unless I specifically say otherwise so I don't get random bugs that take days to track down. I can't help but find concepts from both fields being useful.
    – user4779
    Commented May 23, 2020 at 13:54
  • sure, its Anemic Domain Objects with the business logic in processor classes.
    – Ewan
    Commented May 23, 2020 at 14:39
  • 1
    @user4779: A compromise isn't always the best solution. Sometimes, it's better to (at least predominantly) commit to one over the other. A spoon and a fork are both great utensils, but a spork is both an inferior spoon and an inferior fork.
    – Flater
    Commented May 23, 2020 at 22:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.