In the 2015 WWDC they gave this interesting example demonstrating problems with reference types:
Class Temperature{
var celsius: Double = 0
var fahrenheit: Double{
get{ return celsius * 9 / 5 + 32 }
set{ celsius = (newValue - 32) * 5 / 9 }
}
}let home = House()
let temp = Temperature()
temp.fahrenheit = 75
home.thermostat.temperature = temptemp.fahrenheit = 425
home.oven.temperature = temp
home.oven.bake
And then (ignoring the questionable aspects of the code in general) you wonder why your house is on fire. And this is the problem with reference types, or so the argument goes.
However what I see here is just plain old bad code which isn't using encapsulation properly. It doesn't actually represent a realistic problem, which would be more like:
func myFunc(temp: Temperature) -> Temperature {
// do some calculations on temp then return it.
}var t = myFunc(home.thermostat.temperature)
and now we have a real problem, because it isn't immediately obvious that what was passed into myFunc came from a reference that we really shouldn't have been modifying. So.. copy-on-write, right?
Except if we do that now we've created another problem by exposing the mutator to reference count values. Many reference counts that it was previously possible for the reference counting analysis to eliminate are no longer safe to do so, which kills performance when using reference types.
So what do we do? Make everything into structs? That's the official answer, but it's a really dumb reason for turning something into a struct, and also not practical in a lot of cases. In the remaining cases you're just stuck with bad performance, too bad.
The reality, though, is that we're solving a problem at run time that could have been solved at compile time.
From here on I'm going to ignore the first example above because it's a complete newbie mistake and not something that should dictate language design. Instead I'll focus on the second example and how we can prevent those kinds of issues without resorting to hacking the memory manager and trampling on reference count elimination.
My answer is to create a new visibility type: visible.
So now we have:
Class Thermostat{
visible var temperature: Temperature{
// getter-setter code
}
}
The idea is that declaring a property as 'visible' marks it as public-read-only. If you try to assign it to a var (or pass it to a function as one), it throws an error. Note that we can still do:
house.thermostat.temperature.fahrenheit = 75
because it exports a setter which localizes the direct access for us, but if it didn't then that would also be an error. On the other hand if we try to do, say:
house.oven.temperature = house.thermostat.temperature
we get an error because we've tried to assign a visible type to a var in another object.
Basically it's like throwing "let" in front of it when getting the property, while still allowing conditional write access as long as we provide a setter for it and access that setter directly through the owning object. This prevents what we really wanted to prevent, which is accidental overwriting of reference types, without also preventing intentional overwriting or killing performance.
(No, this doesn't actually work quite right because we have to call the setter 'fahrenheit' to set the value properly, which either requires changing the semantics of setters or changing the way it's written which could be done in any of several different ways. The basic idea still stands though. Checks should be during compile time not run time wherever possible.)