<div dir="ltr"><span>&quot;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.&quot;<br></span><br>I am not sure that this is true. Do you really see no value in completely hiding implementation details of an API and making it enforceable by the compiler?<br><br><div class="gmail_quote"><div dir="ltr">On Sun, Dec 13, 2015 at 8:53 PM Brent Royal-Gordon via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a>&gt; wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">&gt;&gt; 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.<br>
&gt;<br>
&gt; I meant zero performance cost.  Of course all features have &quot;cost&quot; if we mean &quot;cognitive overhead&quot;.  Type safety for example has a huge cognitive overhead.  Think back to the days of &quot;Bool is not an NSString&quot;.  But the benefit of typesafety is large.<br>
&gt;<br>
&gt; 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.<br>
<br>
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`.<br>
<br>
I believe that *most*—certainly not all, but most—uses of `local` are like this. `local` would improve the code&#39;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.<br>
<br>
&gt;&gt; many people, when they do that, find this idea wanting.<br>
&gt;<br>
&gt; Who?  You?  Then build an argument around that.  I don&#39;t know who &quot;many people&quot; are or what their justification is.<br>
<br>
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.<br>
<br>
&gt; 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.<br>
&gt;<br>
&gt; On B, we seem to agree:<br>
&gt;<br>
&gt;&gt;  it might be difficult to construct a Synchronized instance correctly.<br>
&gt;<br>
&gt; 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&#39;t have a need for it (or even &quot;many people&quot;) is not really a counterargument I am able to understand.<br>
<br>
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:<br>
<br>
        class Synchronized&lt;Value&gt; {<br>
                init(value: Value, mutableRunner: (Void -&gt; Void) -&gt; Void, immutableRunner: (Void -&gt; Void) -&gt; Void) { … }<br>
                …<br>
        }<br>
<br>
So I’m going to think out loud about this for a minute. All code was written in Mail.app and is untested.<br>
<br>
I suppose we start with a protocol. The ideal interface for Synchronized would look something like this:<br>
<br>
        protocol SynchronizedType: class {<br>
                typealias Value<br>
                func withMutableValue&lt;R&gt;(mutator: (inout Value) throws -&gt; R) rethrows -&gt; R<br>
                func withValue&lt;R&gt;(accessor: Value throws -&gt; R) rethrows -&gt; R<br>
        }<br>
<br>
You could obviously write separate types like:<br>
<br>
        class QueueSynchronized&lt;Value&gt;: SynchronizedType {<br>
                private var value: Value<br>
                private let queue: dispatch_queue_t<br>
<br>
                init(value: Value, queue: dispatch_queue_t = dispatch_queue_create(“QueueSynchronized”, DISPATCH_QUEUE_CONCURRENT)) {<br>
                        self.value = value<br>
                        self.queue = queue<br>
                }<br>
<br>
                func withMutableValue&lt;R&gt;(@noescape mutator: (inout Value) throws -&gt; R) rethrows -&gt; R {<br>
                        var ret: R?<br>
                        var blockError: NSError?<br>
                        dispatch_barrier_sync(queue) {<br>
                                do {<br>
                                        ret = try mutator(&amp;value)<br>
                                }<br>
                                catch {<br>
                                        blockError = error<br>
                                }<br>
                        }<br>
                        if let error = blockError {<br>
                                throw error<br>
                        }<br>
                        return ret!<br>
                }<br>
<br>
                func withValue&lt;R&gt;(@noescape accessor: Value throws -&gt; R) rethrows -&gt; R {<br>
                        var ret: R?<br>
                        var blockError: NSError?<br>
                        dispatch_sync(queue) {<br>
                                do {<br>
                                        ret = try accessor(value)<br>
                                }<br>
                                catch {<br>
                                        blockError = error<br>
                                }<br>
                        }<br>
                        if let error = blockError {<br>
                                throw error<br>
                        }<br>
                        return ret!<br>
                }<br>
        }<br>
<br>
and:<br>
<br>
        class NSLockSynchronized&lt;Value&gt;: SynchronizedType {<br>
                private var value: Value<br>
                private var lock: NSLock<br>
<br>
                init(value: Value, lock: NSLock = NSLock()) {<br>
                        self.value = value<br>
                        self.lock = lock<br>
                }<br>
<br>
                func withMutableValue&lt;R&gt;(@noescape mutator: (inout Value) throws -&gt; R) rethrows -&gt; R {<br>
                        lock.lock()<br>
                        defer { lock.unlock() }<br>
<br>
                        return try mutator(&amp;value)<br>
                }<br>
<br>
                func withValue&lt;R&gt;(@noescape accessor: Value throws -&gt; R) rethrows -&gt; R {<br>
                        // XXX I don’t know how to get concurrent reads with Cocoa locks.<br>
                        lock.lock()<br>
                        defer { lock.unlock() }<br>
<br>
                        return try accessor(value)<br>
                }<br>
        }<br>
<br>
But that’s not a very satisfying design—so much boilerplate. Maybe we make the thing you’re synchronizing *on* be the protocol?<br>
<br>
        protocol SynchronizerType: class {<br>
                func synchronizeForReading(@noescape accessor: Void -&gt; Void)<br>
                func synchronizeForWriting(@noescape mutator: Void -&gt; Void)<br>
        }<br>
<br>
        final class Synchronized&lt;Value&gt; {<br>
                private var value = Value<br>
                private let synchronizer: SynchronizerType<br>
<br>
                init(value: Value, on synchronizer: SynchronizerType) {<br>
                        self.value = value<br>
                        self.synchronizer = synchronizer<br>
                }<br>
<br>
                func withMutableValue&lt;R&gt;(@noescape mutator: (inout Value) throws -&gt; R) rethrows -&gt; R {<br>
                        var ret: R?<br>
                        var blockError: NSError?<br>
                        synchronizer.synchronizeForWriting {<br>
                                do {<br>
                                        ret = try mutator(&amp;value)<br>
                                }<br>
                                catch {<br>
                                        blockError = error<br>
                                }<br>
                        }<br>
                        if let error = blockError {<br>
                                throw error<br>
                        }<br>
                        return ret!<br>
                }<br>
<br>
                func withValue&lt;R&gt;(@noescape accessor: Value throws -&gt; R) rethrows -&gt; R {<br>
                        var ret: R?<br>
                        var blockError: NSError?<br>
                        synchronizer.synchronizeForReading {<br>
                                do {<br>
                                        ret = try accessor(value)<br>
                                }<br>
                                catch {<br>
                                        blockError = error<br>
                                }<br>
                        }<br>
                        if let error = blockError {<br>
                                throw error<br>
                        }<br>
                        return ret!<br>
                }<br>
        }<br>
<br>
        extension dispatch_queue: SynchronizerType {<br>
                func synchronizeForReading(@noescape accessor: Void -&gt; Void) {<br>
                        dispatch_sync(self, accessor)<br>
                }<br>
<br>
                func synchronizeForWriting(@noescape mutator: Void -&gt; Void) {<br>
                        dispatch_barrier_sync(self, mutator)<br>
                }<br>
        }<br>
<br>
        extension NSLock: SynchronizerType {<br>
                func synchronizeForReading(@noescape accessor: Void -&gt; Void) {<br>
                        // XXX I don’t know how to get concurrent reads with Cocoa locks.<br>
                        lock()<br>
                        accessor()<br>
                        unlock()<br>
                }<br>
<br>
                func synchronizeForWriting(@noescape mutator: Void -&gt; Void) {<br>
                        lock()<br>
                        mutator()<br>
                        unlock()<br>
                }<br>
        }<br>
<br>
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.<br>
<br>
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.<br>
<br>
--<br>
Brent Royal-Gordon<br>
Architechies<br>
<br>
_______________________________________________<br>
swift-evolution mailing list<br>
<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a><br>
<a href="https://lists.swift.org/mailman/listinfo/swift-evolution" rel="noreferrer" target="_blank">https://lists.swift.org/mailman/listinfo/swift-evolution</a><br>
</blockquote></div></div>