[swift-evolution] access control proposal
ilya
ilya.nikokoshev at gmail.com
Mon Dec 14 01:49:09 CST 2015
> I believe that *most*—certainly not all, but most—uses of `local` are
like this. `local` would improve the code's safety, but some sort of
refactoring would be even better. Because of that, I don’t think `local` is
as valuable as you think it is.
+1.
Let's try to promote existing access mechanisms, such as separating code
into a number of files, first, before deciding that we need new keywords.
On Mon, Dec 14, 2015 at 4:53 AM, Brent Royal-Gordon via swift-evolution <
swift-evolution at swift.org> wrote:
> >> But it’s not zero-cost. It’s another thing to learn, another thing to
> think about, another way you have to mentally analyze your code.
> >
> > I meant zero performance cost. Of course all features have "cost" if we
> mean "cognitive overhead". Type safety for example has a huge cognitive
> overhead. Think back to the days of "Bool is not an NSString". But the
> benefit of typesafety is large.
> >
> > In this case, the cognitive overhead is small, and so is the benefit.
> But I think the value-per-unit-cost is similar. In both cases the compiler
> helps you not do something very very bad, that is hard to debug in ObjC.
>
> And I’m saying that I think the benefit is even smaller than you think it
> is, because you can usually get the same benefits by other means, and the
> resulting code will be *even safer* than `local` would give you. Again,
> consider the “value protected by a queue” case. Using `local` here limits
> the amount of code which could contain an access-without-synchronization
> bug, but `Synchronized` *completely eliminates* that class of bugs.
> Synchronized is a *better*, *safer* solution than `local`.
>
> I believe that *most*—certainly not all, but most—uses of `local` are like
> this. `local` would improve the code's safety, but some sort of refactoring
> would be even better. Because of that, I don’t think `local` is as valuable
> as you think it is.
>
> >> many people, when they do that, find this idea wanting.
> >
> > Who? You? Then build an argument around that. I don't know who "many
> people" are or what their justification is.
>
> I’m sorry, I don’t mean to make it sound like I’m speaking for some big,
> ill-defined posse. I just mean that different people will draw the line on
> the necessary cost-to-benefit in different places, and for some, this
> feature will fall on the wrong side of the line. People who don’t like this
> feature don’t misunderstand it; they just have a different subjective
> assessment of its value.
>
> > My justification is essentially that A) something like Synchronized is a
> problem nearly everybody has and B) the difficulty of defining a
> class-based solution in an optimal way.
> >
> > On B, we seem to agree:
> >
> >> it might be difficult to construct a Synchronized instance correctly.
> >
> > So I can only conclude you disagree about A. However, I think my A is
> much stronger than is strictly necessary to demand a language feature.
> There are plenty of language features that not everyone uses, so the fact
> that you don't have a need for it (or even "many people") is not really a
> counterargument I am able to understand.
>
> As far as a standard Synchronized is concerned, I agree with you that it’s
> a good idea. I am simply *worried* that we may have trouble coming up with
> a design that’s flexible enough to accommodate multiple synchronization
> methods, but still makes it easy to create a properly-configured
> Synchronized object. For example, something like this would be so
> complicated to configure that it would virtually defeat the purpose of
> having a Synchronized type:
>
> class Synchronized<Value> {
> init(value: Value, mutableRunner: (Void -> Void) -> Void,
> immutableRunner: (Void -> Void) -> Void) { … }
> …
> }
>
> So I’m going to think out loud about this for a minute. All code was
> written in Mail.app and is untested.
>
> I suppose we start with a protocol. The ideal interface for Synchronized
> would look something like this:
>
> protocol SynchronizedType: class {
> typealias Value
> func withMutableValue<R>(mutator: (inout Value) throws ->
> R) rethrows -> R
> func withValue<R>(accessor: Value throws -> R) rethrows ->
> R
> }
>
> You could obviously write separate types like:
>
> class QueueSynchronized<Value>: SynchronizedType {
> private var value: Value
> private let queue: dispatch_queue_t
>
> init(value: Value, queue: dispatch_queue_t =
> dispatch_queue_create(“QueueSynchronized”, DISPATCH_QUEUE_CONCURRENT)) {
> self.value = value
> self.queue = queue
> }
>
> func withMutableValue<R>(@noescape mutator: (inout Value)
> throws -> R) rethrows -> R {
> var ret: R?
> var blockError: NSError?
> dispatch_barrier_sync(queue) {
> do {
> ret = try mutator(&value)
> }
> catch {
> blockError = error
> }
> }
> if let error = blockError {
> throw error
> }
> return ret!
> }
>
> func withValue<R>(@noescape accessor: Value throws -> R)
> rethrows -> R {
> var ret: R?
> var blockError: NSError?
> dispatch_sync(queue) {
> do {
> ret = try accessor(value)
> }
> catch {
> blockError = error
> }
> }
> if let error = blockError {
> throw error
> }
> return ret!
> }
> }
>
> and:
>
> class NSLockSynchronized<Value>: SynchronizedType {
> private var value: Value
> private var lock: NSLock
>
> init(value: Value, lock: NSLock = NSLock()) {
> self.value = value
> self.lock = lock
> }
>
> func withMutableValue<R>(@noescape mutator: (inout Value)
> throws -> R) rethrows -> R {
> lock.lock()
> defer { lock.unlock() }
>
> return try mutator(&value)
> }
>
> func withValue<R>(@noescape accessor: Value throws -> R)
> rethrows -> R {
> // XXX I don’t know how to get concurrent reads
> with Cocoa locks.
> lock.lock()
> defer { lock.unlock() }
>
> return try accessor(value)
> }
> }
>
> But that’s not a very satisfying design—so much boilerplate. Maybe we make
> the thing you’re synchronizing *on* be the protocol?
>
> protocol SynchronizerType: class {
> func synchronizeForReading(@noescape accessor: Void ->
> Void)
> func synchronizeForWriting(@noescape mutator: Void -> Void)
> }
>
> final class Synchronized<Value> {
> private var value = Value
> private let synchronizer: SynchronizerType
>
> init(value: Value, on synchronizer: SynchronizerType) {
> self.value = value
> self.synchronizer = synchronizer
> }
>
> func withMutableValue<R>(@noescape mutator: (inout Value)
> throws -> R) rethrows -> R {
> var ret: R?
> var blockError: NSError?
> synchronizer.synchronizeForWriting {
> do {
> ret = try mutator(&value)
> }
> catch {
> blockError = error
> }
> }
> if let error = blockError {
> throw error
> }
> return ret!
> }
>
> func withValue<R>(@noescape accessor: Value throws -> R)
> rethrows -> R {
> var ret: R?
> var blockError: NSError?
> synchronizer.synchronizeForReading {
> do {
> ret = try accessor(value)
> }
> catch {
> blockError = error
> }
> }
> if let error = blockError {
> throw error
> }
> return ret!
> }
> }
>
> extension dispatch_queue: SynchronizerType {
> func synchronizeForReading(@noescape accessor: Void ->
> Void) {
> dispatch_sync(self, accessor)
> }
>
> func synchronizeForWriting(@noescape mutator: Void ->
> Void) {
> dispatch_barrier_sync(self, mutator)
> }
> }
>
> extension NSLock: SynchronizerType {
> func synchronizeForReading(@noescape accessor: Void ->
> Void) {
> // XXX I don’t know how to get concurrent reads
> with Cocoa locks.
> lock()
> accessor()
> unlock()
> }
>
> func synchronizeForWriting(@noescape mutator: Void ->
> Void) {
> lock()
> mutator()
> unlock()
> }
> }
>
> Huh. That’s…actually pretty clean. And I suppose if you want it to default
> to using a dispatch queue, that’s just a default value on
> Synchronized.init(value:on:), or a new init(value:) added in an extension
> in LibDispatch. I thought this would be a lot hairier.
>
> So, yeah, I guess I like the idea of a standard Synchronized. A
> well-designed version (one with something like SynchronizerType) can work
> with multiple locking mechanisms, without resorting to something
> error-prone like passing in closures. +1 for that.
>
> --
> Brent Royal-Gordon
> Architechies
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20151214/6de5175a/attachment.html>
More information about the swift-evolution
mailing list