[swift-evolution] [Review] SE-0005 Better Translation of Objective-C APIs Into Swift
Dave Abrahams
dabrahams at apple.com
Tue Feb 2 12:39:18 CST 2016
on Mon Feb 01 2016, Douglas Gregor <swift-evolution at swift.org> wrote:
>> On Feb 1, 2016, at 3:26 PM, Radosław Pietruszewski
>> <radexpl at gmail.com> wrote:
>>
>>>>
>>>> Here’s another observation:
>
>>>>
>>>> I noticed that there’s another word commonly used to join words in
>>>> ObjC method names, very similarly to “with”, and that’s “using”.
>>>>
>>>> The vast majority of those is the phrase “usingBlock”:
>>>>
>>>> enumerateObjectsUsingBlock(_:)
>>>>
>>>> How will this be handled? If I understand correctly, “block” will
>>>> be dropped as it’s repeating type information, so we’d get:
>>>>
>>>> enumerateObjectsUsing(_:)
>>>>
>>>> But perhaps it would make more sense to split by “using” the same
>>>> way as “with”, yielding:
>>>>
>>>> enumerateObjects(block:)
>>>
>>> It's worth giving it a try. Here are the results:
>>>
>>> https://github.com/apple/swift-3-api-guidelines-review/pull/8
>>> <https://github.com/apple/swift-3-api-guidelines-review/pull/8>
>>>
>>> They look generally consistent with the “use a first argument label
>>> when the first parameter isn’t the direct object” guidelines being
>>> discussed.
>>
>> Awesome, thank you. The result seems overwhelmingly positive to my eyes.
>
> I’m not quite as convinced. The function/block/selector cases work
> extremely well, but cases like this:
>
> - func connectNodeUsingObstacles(node: GKGraphNode2D)
> + func connectNode(obstacles node: GKGraphNode2D)
>
> don’t necessarily seem like a win: it’s completely fundamental to the
> method’s operation that it’s avoiding those obstacles, and I think the
> original is better than the update here.
I think this is arguable. What if there was, in addition to obstacles,
a collection of swampy regions that can slow down progress? These seem
like peer parameters to the general problem of path finding, the
receiving class' name (GKObstacleGraph) notwithstanding.
>>>> Possibly dropping the label altogether (regardless of the
>>>> position?) since it’s also redundant? Not sure:
>>>>
>>>> enumerateObjects(_:)
>>>
>>> I don't think I would drop the label. The first parameter is not
>>> the direct object of the verb enumerate, nor is it described by
>>> "Objects”.
>>
>> OK, agree. FWIW, when the passed block is the only argument, many
>> people (me included) will generally use it using the trailing
>> closure syntax, and the label will be dropped this way anyway.
>
> Yes, and I think it’s better not to have the “block” or “using” in the
> base name when you’re using trailing closure syntax. However, when
> you’re providing a function (not as a trailing closure), that function
> isn’t the direct object, so it should have a first argument label.
>
> - Doug
>
>>>
>>>> But I also see more cases that don’t involve blocks:
>>>>
>>>> dataUsingEncoding(_:)
>>>> connectNodeUsingObstacles(_:)
>>>> canMakePaymentsUsingNetworks(_:)
>>>> awakeAfterUsingCoder(_:)
>>>> filteredArrayUsingPredicate(_:)
>>>> keysSortedByValueUsingComparator(_:)
>>>>
>>>> Would become:
>>>>
>>>> data(encoding:)
>>>> connectNode(obstacles:)
>>>> canMakePayments(networks:)
>>>> awakeAfter(coder:)
>>>> filteredArray(predicate:)
>>>> keysSortedByValue(comparator:)
>>>>
>>>> What do you think?
>>>
>>> I didn’t dig through the results as much as I’d like, but this makes me *really* want to standardize the name of closure arguments:
>>>
>>> - func sortSubviewsUsing(compare: @convention(c) (NSView, NSView, UnsafeMutablePointer<Void>) -> ComparisonResult, context: UnsafeMutablePointer<Void>)
>>> + func sortSubviews(function compare: @convention(c) (NSView, NSView, UnsafeMutablePointer<Void>) -> ComparisonResult, context: UnsafeMutablePointer<Void>)
>>>
>>> - func enumerateAvailableRowViewsUsing(handler: (NSTableRowView, Int) -> Void)
>>> + func enumerateAvailableRowViews(block handler: (NSTableRowView, Int) -> Void)
>>>
>>>
>>> - Doug
>>>
>>>
>>>> — Radek
>>>>
>>>>> On 27 Jan 2016, at 08:50, Douglas Gregor
>>>>> <dgregor at apple.com
>>>>> <mailto:dgregor at apple.com>> wrote:
>>>>>
>>>>>>
>>>>>> On Jan 25, 2016, at 6:55 AM, Radosław Pietruszewski
>>>>>> <radexpl at gmail.com
>>>>>> <mailto:radexpl at gmail.com>> wrote:
>>>>>>
>>>>>> Hello all,
>>>>>>
>>>>>> I’m overwhelmingly *for* this proposal. I think removing
>>>>>> needless verbosity and keeping the signal-to-noise ratio high is
>>>>>> one of the most immediately appealing aspects of Swift, as well
>>>>>> as a great general improvement to the programming experience.
>>>>>>
>>>>>> And so unswiftified (yes, it’s a word now) APIs from Objective-C
>>>>>> stick out like a sore thumb. Not only are they harder to read
>>>>>> and write, they visually overwhelm the less verbose,
>>>>>> information-dense Swift-first code.
>>>>>>
>>>>>> Just like previous 1.0—2.0 attempts at bridging the gap (with
>>>>>> NSError params being translated to Swift errors, factory methods
>>>>>> translated to initializers, etc.), automating this will be an
>>>>>> error-prone process, and almost bound to be a bit annoying at
>>>>>> first, before all the glitches and poor translations are
>>>>>> smoothed out. And yet I feel like just like the previous
>>>>>> automated translations were overwhelmingly a great thing, so
>>>>>> will the result of this proposal.
>>>>>>
>>>>>> * * *
>>>>>>
>>>>>>> Add First Argument Labels
>>>>>>>
>>>>>>> - func enumerateObjectsWith(_: NSEnumerationOptions = [],
>>>>>>> using: (AnyObject, UnsafeMutablePointer) -> Void)
>>>>>>> + func enumerateObjects(options _: NSEnumerationOptions =
>>>>>>> [], using: (AnyObject, UnsafeMutablePointer) -> Void)
>>>>>>
>>>>>> Good! The Guidelines recommend an explicit first parameter label
>>>>>> for arguments with a default value, but this is a good change
>>>>>> also for another reason, a use case not included in the
>>>>>> Guidelines (I have more to say about this in the SE-0023
>>>>>> thread):
>>>>>>
>>>>>> “Options” is the description of the parameter, not the method
>>>>>> itself. Even if (for whatever reason!) `options` didn’t have a
>>>>>> default value and the word “Options” wasn’t omitted in the
>>>>>> translation,
>>>>>>
>>>>>> enumerateObjects(options: …)
>>>>>>
>>>>>> would be clearer than
>>>>>>
>>>>>> enumerateObjectsWithOptions(…)
>>>>>>
>>>>>> It’s not even about the extra word, about the four useless
>>>>>> characters, it’s simply that “WithOptions” doesn’t describe the
>>>>>> operation at all. It’s a word that conveys no information
>>>>>> (“with”), and “options”, which describes the first parameter. In
>>>>>> Objective-C, there’s no such thing as parameter labels, it’s all
>>>>>> one name, so “With” is used as a separator. But in Swift, making
>>>>>> the first parameter’s label explicit just makes more sense.
>>>>>
>>>>> That’s an interesting thought! If “with” is truly used as a
>>>>> convention for separating the description of the operation from
>>>>> the description of the first parameter, that’s something that can
>>>>> be codified in the Clang importer. I was curious, so I hacked it
>>>>> up. Here’s a diff of the Cocoa APIs that shows what things would
>>>>> look like if we treated “with” as a separator:
>>>>>
>>>>> https://github.com/apple/swift-3-api-guidelines-review/pull/5/files
>>>>> <https://github.com/apple/swift-3-api-guidelines-review/pull/5/files>
>>>>>
>>>>> It’s a diff against SE-0005, and it introduces a significant
>>>>> number of first argument labels. Indeed, you’ll need to grab the
>>>>> patch to see them all:
>>>>>
>>>>> https://github.com/apple/swift-3-api-guidelines-review/pull/5.patch
>>>>> <https://github.com/apple/swift-3-api-guidelines-review/pull/5.patch>
>>>>>
>>>>> A brief survey shows that some cases seem to be lining up with
>>>>> the guideline proposals that have been under discussion. For
>>>>> example, the patch includes:
>>>>>
>>>>> - func fillWith(blendMode: CGBlendMode, alpha: CGFloat)
>>>>> - func strokeWith(blendMode: CGBlendMode, alpha: CGFloat)
>>>>> + func fill(blendMode blendMode: CGBlendMode, alpha: CGFloat)
>>>>> + func stroke(blendMode blendMode: CGBlendMode, alpha: CGFloat)
>>>>>
>>>>> - func encodeWith(aCoder: Coder)
>>>>> + func encode(coder aCoder: Coder)
>>>>>
>>>>> which you might recognize, because it’s the example you used:
>>>>>
>>>>>> And with that in mind, I object to these translations:
>>>>>>
>>>>>> func fillWith(_: CGBlendMode, alpha: CGFloat)
>>>>>> func strokeWith(_: CGBlendMode, alpha: CGFloat)
>>>>>> func encodeWith(_: Coder)
>>>>>>
>>>>>> Even though these don’t have default values, I believe this
>>>>>> version to be clearer and make more sense, even if slightly more
>>>>>> verbose:
>>>>>>
>>>>>> func fill(blendMode: CGBlendMode, alpha: CGFloat)
>>>>>> func stroke(blendMode: CGBlendMode, alpha: CGFloat)
>>>>>> func encode(coder: Coder)
>>>>>
>>>>> Another random interesting example I encountered:
>>>>>
>>>>> - func addArcWithCenter(center: CGPoint, radius: CGFloat, startAngle: CGFloat, endAngle: CGFloat, clockwise: Bool)
>>>>> + func addArc(center center: CGPoint, radius: CGFloat, startAngle: CGFloat, endAngle: CGFloat, clockwise: Bool)
>>>>>
>>>>> which seems to match the idea behind Erica’s "semantic relationship between the parameters is stronger than their relation to the operation” (or Paul Cantrell’s similar notion of "the direct object is several args taken together”, which feels more in line with the way the API guidelines are written).
>>>>>
>>>>> There’s also this:
>>>>>
>>>>> - func tracksWithMediaType(mediaType: String) -> [AVMovieTrack]
>>>>> + func tracks(mediaType mediaType: String) -> [AVMovieTrack]
>>>>>
>>>>> - func tracksWithMediaCharacteristic(mediaCharacteristic: String) ->
>>>>> + func tracks(mediaCharacteristic mediaCharacteristic: String) -> [AVMovieTrack]
>>>>>
>>>>> which feels reminiscent of Paul’s “resource” example:
>>>>>
>>>>> service.resource("/foo")
>>>>> service.resource(absoluteURL: "http://bar.com <http://bar.com/>")
>>>>> service.resource(absoluteURL: NSURL(string: "http://bar.com <http://bar.com/>"))
>>>>>
>>>>> where (I think) the argument is that the various methods should all have the same base name because they’re all returning “tracks” or a “resource”, respectively.
>>>>>
>>>>> There is a ton of data in that patch. I’d be interested to hear whether the resulting Cocoa APIs feel better in Swift—are they following the evolving set of guidelines for first argument labels that are under discussion, and are the resulting APIs clearer/more Swifty? What specific APIs work well and where does this “with-as-separator” heuristic break down?
>>>>>
>>>>> - Doug
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
--
-Dave
More information about the swift-evolution
mailing list