4

While watching a youtube video on Value Types in Swift, I was surprised by a simple example (around the 3:00 minute mark) that was given to demonstrate the pitfalls of reference types.

Example code:

let home = House()
let temp = Temperature()
temp.fahrenheit = 75
home.thermostat.temperature = temp.copy()

temp.fahrenheit = 425
home.oven.temperature = temp.copy()
home.oven.bake()

In the above code, House and Temperature are classes and therefore have reference type semantics. The justification for invoking the copy() method in several places is to ensure that mutating the temp variable for setting the oven doesn't modify the temperature of the house's thermostat (i.e. prevent unintended mutation due to reference semantics).

I personally find this a very strange coding style, but the presenter goes on for several minutes about how this so-called "defensive copying" is used throughout Apple's core librairies to prevent bugs so I'm wondering if I've been doing things wrong.

Personally, I would have written this along the following lines:

let home = House()
home.thermostat.temperature = Temperature()
home.thermostat.temperature.fahrenheit = 75   //actually I would personally have supplied this as a parameter to the initializer but I'm trying to remain as close to the original code as possible.

home.oven.temperature = Temperature()
home.oven.temperature.fahrenheit = 425
home.oven.bake()

In other words, I would explicitly create new instances of the Temperature class by calling its initializer (constructor) instead of reusing a local variable of that type and assigning copies of it. Not only does it avoid the last copy, which is useless, but the code seems clearer to me.

Now granted, this is a trivial example. In the case of a complex class with many members that share the same values between instances, recreating a new type from scratch and assigning every value over and over is a lot of programming overhead. However, in my experience at least, I've usually created initializers or factory methods that perform these common assignments so I only have to make a few changes where needed.

In the rare cases where the above doesn't apply, I find that explicitly calling a clone() or copy() method is fine. By only doing so rarely, it conveys the fact to me that something special is going on (e.g. deep copy of a tree for example).

Therefore, my question is: is Apple's above code a recommended practice? And if not, in what circumstances (other than deep copies) would manual copying as above be the preferred way of doing things?

  • 2
    Defensive copying to get immutable semantics is a well-known, tried and true technique. If you want behavior that is detached from the original object reference, you have to make a copy. That's it. C++ even uses "copy semantics" as a synonym for "value semantics." – Robert Harvey Mar 30 '16 at 19:57
  • They've potentially over-simplified to make the idea simple to understand at the cost of providing a good use-case. By using copy, they protect against future changes to a class - so you don't need to update code to copy new members. – Phil Lello Mar 30 '16 at 20:10
  • @RobertHarvey But then C++ also introduced move semantics in order to lower the cost for the many copies that are made. If (in the absence of move semantics) all your copies are deep, excessive defensive copying might be something you can't or don't want to afford. – 5gon12eder Mar 30 '16 at 22:38
  • This is precisely the reason why I like the C approach of everything is call-by-value. With that, Home_setTemperature(home, temp); is guaranteed 1. not to modify temp, and 2. not to invoke funny effects when temp is changed afterwards. However, when I see Home_setTemperature(home, &temp); in C, I immediately suspect that either guarantee does not hold. Pity that this clarity is lost with "pointer-free" languages. – cmaster Mar 30 '16 at 22:46
4

I agree with Phil Lello that this has probably been over-simplified, possibly to the point that its confusing. A much clearer example of the need and undesirability of (needing to do) defensive copying is the following using C# for concreteness.

class House {
    private Thermostat thermostat;
    private ControlDisplay cc;
    // ...
    public void ShowDisplay() {
        // ...
        cd.SetTemperatureField(thermostat.Temperature.Clone());
        // ...
        cd.UpdateDisplay();
    }
}

Now, if Temperature is mutable, every time we want to show the control display we need to copy the thermostat's temperature even though it is unlikely to be changed by the display. Even if we control the code for ControlDisplay and thus can verify by looking at the source code that it doesn't mutate the passed in Temperature, we still copy because that may be changed in the future. There was no way to communicate and enforce that it shouldn't be changed.

If Temperature were immutable, on the other hand, there would be no need for this copying. The result would be cleaner, more efficient code, that simply makes certain mistakes and couplings impossible.

I imagine most of the instances in the Apple's core libraries are more like the above than like the example provided in the video. I can only imagine a few, somewhat odd, scenarios where code like the original example might conceivably be preferable to code more like your second example (or your alluded to rewriting).

  • Thanks for providing a more substantive example of where defensive copying would be used. – Dragonspell Apr 3 '16 at 13:43
4

So I watched the video. The example initially given is beyond ludicrous. If anything, it's primary use should be an example of a bug in MyFirstProgram due to variable reuse. Later examples (which were glossed over) more directly illustrate uses for defensive copying.

However, defensive copying was not the point of the video. Nor is the example (original or defensive copying) code a model to be followed. And in fact, the presenter even says as much starting at 3:59 in the video. He's basically setting up a problem (poorly) to explain how they solved it with Swift. If you look at 19:45, he shows that the example code would not even compile in Swift. And starting at 20:03 he shows that the defensive copy method isn't needed. (Although his example there looks horrifyingly similar to the original code... with one let changed to var. o:) Along the way he also manages to brow-beat Haskell with one cherry-picked algorithm and a singular acceptance criteria.

In summary, the take-away from segments of the video with that code are:

  • You should not be writing code like in the example
  • If you write code like this, it will not compile in Swift
  • The subsequent defensive-copy code "fixes the reference problem" but is too much work (3:59)
  • You don't have to write this defensive copied code because of the way Swift implements value semantics
  • The examples in this presentation are not very good.

My guess at thoughts leading up to this presentation.

  • "You know, we really should encourage more variable reuse."
  • "If one more person asks why Swift doesn't have the functional paradigm, I'm gonna completely troll FP at WWDC."
  • +1 for watching the video and summarizing those key take-aways. I definitely felt the same way after watching the video. – Dragonspell Apr 3 '16 at 13:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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