[swift-evolution] Pitch: Progress Tracking in Swift

cocoadev at charlessoft.com cocoadev at charlessoft.com
Tue Jan 19 01:26:26 CST 2016


On 2016-01-19 00:35, Brent Royal-Gordon wrote:
>> What do you all think?
> 
> I think this is really thin syntactic sugar over `-> NSProgress`, and
> doesn't add enough value to justify itself. Unlike the error handling
> system:
> 
> - The fact that a function *can* provide progress doesn't mean that
> any particular caller needs that progress.
> - Progress monitoring doesn't require a lot of convoluted control flow.
> - Progress monitoring doesn't require convoluted ways of returning the
> progress, since most progress-reporting operations are asynchronous
> and have no other immediate return value.

This all seems to be under the assumption that NSProgress is an API 
which is returned to the calling function. This is not how NSProgress 
works, and in my proposal, progress objects wouldn't be returned; they'd 
be passed in as a parameter and added as a child to the current progress 
object. So it's syntactic sugar, yes, but rather than being over -> 
NSProgress, it's over really quite a lot more than that.

The proposal would reduce this (explicit style):

func doSomethingWithProgress(progress: NSProgress) {
     progress.totalUnitCount = 10

     let childProgress1 = NSProgress()
     progress.addChild(childProgress1, withPendingUnitCount: 5)
     childFunc1WithProgress(childProgress1)

     let childProgress2 = NSProgress()
     progress.addChild(childProgress2, withPendingUnitCount: 5)
     childFunc2WithProgress(childProgress2)
}

func childFunc1WithProgress(progress: NSProgress) {
     progress.totalUnitCount = 10

     for i in 0..<10 {
         // do some work
         progress.completedUnitCount += 1
     }
}

(childFunc2WithProgress looks like childFunc1WithProgress)

or this (implicit style):

func doSomethingWithProgress() {
     let progress = NSProgress(totalUnitCount: 10)

     progress.becomeCurrentWithPendingUnitCount(5)
     childFunc1()
     progress.resignCurrent()

     progress.becomeCurrentWithPendingUnitCount(5)
     childFunc2()
     progress.resignCurrent()
}

func childFunc1() {
     let progress = NSProgress(totalUnitCount: 10)

     for i in 0..<10 {
         progress.completedUnitCount += 1
     }
}

(childFunc2 looks like childFunc1)

to simply this:

func doSomethingWithProgress() reports {
     progress.prepare(10)

     report(5) childFunc1()
     report(5) childFunc2()
}

func childFunc1() reports {
     progress.prepare(something)

     for i in 0..<10 {
         progress.completed += 1
     }
}

(childFunc2 looks like childFunc1)

I don't know about you, but this seems a lot cleaner, and a lot more 
Swifty, to me. It also combines the safety of the explicit method with 
the flexibility of the implicit method, while requiring a heck of a lot 
less code than both.

> - Progress monitoring is actually done fairly infrequently.

If the progress object were intelligent and efficient enough to not send 
notifications on the current thread, the progress object could be 
updated a lot more than NSProgress is able to. You could optionally 
apply other optimizations as well, such as coalescing updates rather 
than firing notifications each time, which would reduce the cost of 
updating the progress object to a few integer stores and could possibly 
cut down some more on the code you'd need to clutter your worker loop 
with.

> - There is no need to customize the progress monitoring system in the
> way that, for instance, `ErrorType` allows.

I don't know about that; the whole reason I came up with this idea in 
the first place was because I was wishing I could customize NSProgress 
in certain ways, the inefficient notification system being chief among 
them.

> I think that all Swift needs to support progress monitoring is to make
> sure that `NSProgress` returns can be ignored without a warning even
> if we make `@warn_unused_return` the default, and perhaps to overload
> `+=` on two `NSProgress`es if we can find an appropriate way to do it.

Again, NSProgress objects are passed in, not returned. += really would 
have no meaning that I can see for NSProgress.

Charles



More information about the swift-evolution mailing list