Skip to content

Devil is in the details II: immutable collections

Inmmutability is good. Just yesterday, we were discussing how good it is. But just in case, here is another example.

Let’s assume we are building the software to manage a company’s payroll. We have only these requirements:

  • The Human Resources Department should be able to hire and fire people.
  • The Human Resources Department needs to validate that a Person has a valid id before hiring her (there are very strict immigration regulations in here).
  • The Human Resources Department should be able to produce an up-to-date list of employees at any moment in time.

The simplest thing that could possibly work

With those requirements in mind, this the simplest thing we can build. A Person would look like this:

And the Human Resources Department would look like this:

Notice how we do some fancy validation when hiring a new person, and throw an error if anything goes wrong. Also notice how the employees arrary is public, because HR need to produce it upon request.

The devil is in the details

We can hire someone:

We can try to hire someone without a valid Id, and HR would throw an error:

But, we can also bypass the ID validation by modifying the employees array directly:

Boom!

Public collections should be immutable

This is, obviously, a very convoluted example. But the thing is that when we do not enforce immutability, bad things may happen, either by distraction, by means of unexpected side effects, or just by means of malicious or misbehaving code.

One way, and it is not the only possible way, to enforce immutability in the precious example might be this:

Notice how we made the mutable collection private, and we provide a readonly property (employees) that returns an immutable collection, built upon request. But, remember, what matters here is not how we create that immutable collection, but the fact that the public API of the HR class provides only immutable data.

5 Comments

  1. No need for internalEmployeeList.

    Simply use private(set) var employees: [Person] = [] and you’re good.

    • sexysoftwareengineer sexysoftwareengineer

      I am afraid that’s not the case. You would still be able to append items to the employees array directly, which is precisely what we should want to avoid

      • No you can’t unless you do from within the same Swift file.
        “private(set)” limits setting the variable to call sites from within the same file.

        If that’s what’s happening in your case then “internalEmployeesList” is also accessible.

        If done right then you’ll get the following error message:

        error: cannot use mutating member on immutable value: 'employees' setter is inaccessible
        hr.employees.append(anotherPersonWithoutValidID)

        • sexysoftwareengineer sexysoftwareengineer

          True, but still, in that case, that property would be mutable, wouldn’t it?

          • No, in normal projects it is not mutable.
            You’re likely testing your code in a single Playgrounds file which leads to the behavior you observe. In real projects you usually have HumanResources defined in a different file than from where the class is actually used. In that case “private” modifiers would work then.

            I quickly made a Playground to highlight the proper behavior: https://www.dropbox.com/s/my2inuhnbizmijt/HR.playground.zip?dl=0

Leave a Reply

Your email address will not be published. Required fields are marked *