> On Jul 9, 2016, at 9:08 AM, Austin Zheng via swift-evolution
> <swift-evolution@swift.org> wrote:
>
>> On 9 Jul 2016, at 00:53, Jon Shier <j...@jonshier.com
>> <mailto:j...@jonshier.com>> wrote:
>>
>>> While I can see why removing the labels from the type system would be a
>>> good idea, I don’t see why calling the functions with labels would be
>>> actively prohibited. That’s useful information for the developer to have,
>>> and if the compiler doesn’t know them in some way, you can be assured
>>> Xcode’s autocomplete won’t see them.
>>
>> I wish the core team or the author of the proposal came to this thread and
>> engaged again with the community.
>
> I'm not inclined to spend time engaging with people who couldn't be bothered
> to give feedback during the week-long official review period.
Not all people "couldn’t be bothered” but had life events, such as moving
across states with four kids, that prevented them from being able to engage
during the official review period.
I’ve read through all of the posts that I see in my mailbox regarding this
topic and I’ve yet to see any real answer to the concerns of tooling, typealias
usage, closures, and code readability and maintainability concerns under this
new proposal. This is the closest I’ve seen (from Douglas Gregor a few days
ago):
> The core team’s intent is that one can add cosmetic labels to function types,
> but that those labels are not (cannot be) used at the call site, e.g.,
Do you have specific post in mind that addresses the these concerns? Maybe I’m
just missing them, but I really don’t see those addressed and they are not
mentioned in the proposal at all.
Let’s say I want to model a problem regarding some library functions that work
with resizing some image type. Today, if I did that, the tooling would give me
auto-completion for all of the parameter labels and the code is very legible.
struct Size {
var x: Int
var y: Int
}
struct Image {
var data: Data
var size: Size
// lots more properties...
}
typealias ImageResizedCallback = (original: Image, resized: Image) -> Void
func doResizeA(image: Image, completed: ImageResizedCallback) {
let newData = image.data
let newSize = image.size
// do some work that's really slow...
completed(original: image, resized: Image(data: newData, size: newSize))
}
func doResizeB(image: Image, completed: (original: Image, resized: Image) ->
Void) {
let newData = image.data
let newSize = image.size
// do some work that's really slow...
completed(original: image, resized: Image(data: newData, size: newSize))
}
In either approach, `doResizeA` with a named callback or `doResizeB` with an
explicit parameter type offer benefits that are lost under this proposal.
let someAsset = Image(data: Data(), size: Size(x: 100, y: 100))
doResizeA(image: someAsset, completed: { (original, resized) in
print("doResizeA - original: \(original), resized: \(resized)")
})
doResizeB(image: someAsset, completed: { (original, resized) in
print("doResizeB - original: \(original), resized: \(resized)")
})
Note that both `original` and `resized` get auto-completed for us here. This
provides great code clarity and insights. This is also self-documenting code.
However, under this proposal as accepted (as I understand it), we are left with
this:
func doResizeC(image: Image, completed: (Image, Image) -> Void) {
let newData = image.data
let newSize = image.size
// do some work that's really slow...
completed(image, Image(data: newData, size: newSize))
}
This code is no longer self-documenting. I have no idea at looking at the call
signature which order the images to `completed` should be.
Further, I get no help here when trying to use it:
doResizeC(image: someAsset) { (<#Image#>, <#Image#>) in
<#code#>
}
What do I label for the two images? This is a workflow regression. Yes, maybe
the underlying model is more correct. However, this comes at a burden to
actually authoring and maintaining the code. To me, that’s a truly unfortunate
cost.
So now I probably write this:
doResizeC(image: someAsset) {
print("doResizeC1 - original: \($0), resized: \($1)")
}
Or, if I happen to know or look-up the order, I can fill in the labels myself:
doResizeC(image: someAsset, completed: { (original, resized) in
print("doResizeC2 - original: \(original), resized: \(resized)")
})
Further, we are able to turn runtime errors into compile-time checks when
refactoring the signatures in the `doResizeA` and `doResizeB` functions - e.g.
change the order of `original` and `resized` and you’ll get the compiler
errors. This no longer happens under this proposal because the labels are
erased. One way around that is to use types to solve this problem, but Swift
provides no convenient way to have two `Image` types that share the same
implementation but are considered as different types (sure, I could box them…).
I can get these benefits back, but now I need to introduce even more complexity
into the system.
protocol ResizeCallbackType {
func resized(original: Image, resized: Image)
}
struct ResizeCallback : ResizeCallbackType {
func resized(original: Image, resized: Image) {
print("resized - original: \(original), resized: \(resized)")
}
}
func doResizeD(image: Image, callback: ResizeCallbackType) {
let newData = image.data
let newSize = image.size
// do some work that's really slow...
callback.resized(original: image, resized: Image(data: newData, size:
newSize))
}
doResizeD(image: someAsset, callback: ResizeCallback())
But who really wants to go through all the protocol effort just to get back to
a place being able to allow for label usage again?
I agree that the proposal provides a “more correct” underlying model for Swift.
However, before being approved, I would have really liked to have seen the
above workflow and what I consider to be code readability and maintainability
regressions addressed.
-David
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution