[swift-evolution] [Proposal] Property behaviors
kevin at sb.org
Fri Dec 18 17:56:20 CST 2015
On Fri, Dec 18, 2015, at 07:18 AM, Michel Fortin wrote:
> Le 17 déc. 2015 à 22:47, Kevin Ballard via swift-evolution <swift-evolution at swift.org> a écrit :
> > Good point. I'm also inclined to say that Michel's motivating example (of a property that requires going through a `synchronized` accessor) would be better modeled just as a type Synchronized<Value> that exposes the `synchronized` accessor. No need for behaviors, you just say
> > var data: Synchronized<T> = ...
> > Behaviors are interesting because they affect how normal property access works. Properties that don't expose normal property accessors aren't really properties at all.
> > The only real downside to the Synchronized<Value> type is there's no way to prevent anyone from copying the value (it could be a class, but then you're paying for the overhead of using a separate object). This same limitation is also one of the problems with adding an Atomic<T> type, which is something I would dearly love to have.
> There's indeed the problem that you can't disallow copying a value, and that makes anything involving a mutex a bit fragile. Even if you append a mutex to a property through a behaviour, can a struct containing that property be safely copied? Or should that behaviour be reserved for properties in classes?
> And then the question about the definition of a "property"? Is it still a property if the getter is inaccessible? Maybe not. But this thread is all about redefining properties.
> The reason I'm suggesting implementing synchronized as a behaviour instead of a type is because I found out with experience that synchronization should be related to variables, not types. Types exists in a vacuum while variables are bound to a context, and a synchronized access pattern should usually stay encapsulated in a particular context (struct or class). A Synchronized<T> should not be copied or passed by reference and used out of its context; a property behaviour makes that just impossible, which is better.
I don't really understand what you're trying to say here. The goal you describe (of not being able to copy or pass the synchronized value around) is satisfied by having some way to declare a struct that cannot be copied (but can be moved, because moves are always fine as long as there's no code (e.g. other threads) that is still expecting the value to be at its original location). Describing synchronized behavior as a non-copyable type like this actually works extremely well in practice.
The Rust language is an excellent demonstration of this fact, where literally all of the threading and synchronization behavior is expressed in the libraries by leveraging the type system and borrow checker to guarantee safety. If you want a value with an associated lock, you just have a value of type Mutex<T>, and the only way to access the underlying value is by using an accessor that returns a MutexGuard, which is a smart pointer (Rust has smart pointers) that lets you access the underlying value and releases the lock when the guard goes out of scope. And because of Rust's borrow checker, it's impossible to leak a reference to the underlying value past the scope of the MutexGuard (and you're not allowed to copy MutexGuards or to send them to other threads, but you can still move the guard around locally and the borrow checker will statically ensure the guard itself cannot outlive the mutex). The Mutex value itself also cannot be copied, but (if you have no outstanding guards) you can move it or even send it to other threads, and you can put it in a location that is visible to multiple threads at once (otherwise what's the point?).
Ultimately, I would love to have non-copyable structs in Swift. But the only way to really do that is to have references in the language (otherwise the moment you call a method on a non-copyable struct, you've lost the struct as the value is moved into the method). And if you have references, you really want them to be safe. Personally, I'd love to have the power of Rust's borrowchecker and lifetimes system, but as I mentioned in another thread recently, there is a pretty steep learning curve there.
More information about the swift-evolution