[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