[Review] SE-0005 Better Translation of Objective-C APIs Into Swift

Doug,

For methods such as:

func description(locale locale: AnyObject?) -> String
func description(locale locale: AnyObject?, indent level: Int) -> String

1. Can the `locale` parameter be given a default `= nil` argument?

2. Can the parameter type be changed to `NSLocale?` when translated into Swift?

-- Ben

Doug,

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:)

Possibly dropping the label altogether (regardless of the position?) since it’s also redundant? Not sure:

   enumerateObjects(_:)

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?

— Radek

···

On 27 Jan 2016, at 08:50, Douglas Gregor <dgregor@apple.com> wrote:

On Jan 25, 2016, at 6:55 AM, Radosław Pietruszewski <radexpl@gmail.com <mailto:radexpl@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:

  Split method base name at the last "with" preposition. by DougGregor · Pull Request #5 · apple/swift-3-api-guidelines-review · GitHub

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

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/&gt;&quot;\)
    service.resource(absoluteURL: NSURL(string: "http://bar.com <http://bar.com/&gt;&quot;\))

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

+1 to Nate's reasoning. I understand this might be quite controversial, and
I appreciate the work that's been done on Foundation, but I am wary about
canonicalizing a "standard library" that cannot fully embrace Swift
features and idioms as such, at least at this point in time. I understand
the desire to make open-source Foundation a standard for cross-platform
development, but this should not be at the expense of libraries which seek
to provide 'Swiftier' solutions to the same problems.

Best,
Austin

···

On Mon, Feb 1, 2016 at 1:10 PM, Nate Cook via swift-evolution < swift-evolution@swift.org> wrote:

This is a concurring opinion with Drew's review, agreeing that we should
reconsider removing the "NS" prefix but providing my own reasons. The
proposal as a whole is exciting and far-reaching, but this particular
change is misguided. My objections are:

*1) The change will elide the different semantic expectations for
Foundation and Swift standard library types.*

Prefix-less Foundation types will blur the different norms and
expectations for Foundation types vs what we have in the Swift standard
library, particularly in regards to value vs reference semantics of
collection types.

The Swift standard library defines Array, Set, and Dictionary as
collection types with value semantics that operate quite differently from
their peers in Foundation. This change will introduce OrderedSet,
CountedSet, HashTable, MapTable, and others, all of which use reference
semantics and therefore don't provide the same set of guarantees about
ownership and immutability.

As an example, the seemingly similar Set and CountedSet types produce
different results from nearly identical code. Swift's Set type has value
semantics, so changes to a copy don't affect the original Set instance:

let simpleSet = Set(["one", "two", "three"])
var copiedSimpleSet = simpleSet
copiedSimpleSet.remove("one")

copiedSimpleSet.contains("one") // false
simpleSet.contains("one") // true

CountedSet (née NSCountedSet), on the other hand, uses reference
semantics, so changes to a copy of an instance. This is true whether the
copy is an explicit one made via assignment or the implicit copy made when
calling a function with CountedSet as a parameter. This example shows how
nearly code nearly identical to the above produces very different results:

let countedSet = CountedSet(array: ["one", "two", "three"])
var copiedCountedSet = countedSet
copiedCountedSet.remove("one")

copiedCountedSet.contains("one") // false
countedSet.contains("one") // false!

Collections constructed from immutable/mutable class clusters (like
OrderedSet and MutableOrderedSet) pose the same problems, since you may be
using a mutable instance of the "immutable" collection. We clearly are
already dealing with this difference today, but eliminating the "NS" prefix
implies that Foundation types are on the same level as the standard
library. This makes the confusion more pronounced and significantly
increases both the learning curve of the Swift & Foundation APIs and the
risk of errors when using different collection types.

*2) The change may stifle the development of more Swift-oriented APIs.*

Foundation types were developed in and for a language that uses reference
semantics and subclassing, rather than value semantics and a
protocol-oriented approach. The designs therefore use and reinforce norms
that relate better to Objective-C than to Swift—class clusters of
non-final, immutable types with mutable subclasses, immutable types with
separate but related mutable counterparts, etc.

A Swift-native CountedSet (and other collections) would surely have value
semantics built in. What about other types—do the new
Calendar/Date/DateComponents types look like the system that we would
design from the ground up in Swift? How about URL and URLComponents?
Dropping the "NS" prefix would make it more difficult to gradually expand
the standard library to encompass bridging versions of these and other
common types.

*3) The change will make it harder to find information about the revised
APIs.*

Excellent search-ability is an unintended benefit of the "NS" prefix, and
can be understood as a way that these types avoid collisions in the vast
namespace-less sea of Internet search results. Searching for help with URL
and Calendar will be much more difficult than their NS'ed counterparts.

Especially given the challenges that this proposal will pose for code
sourced from tutorials, blog posts, Stack Overflow answers, etc., keeping
at least the class names as sign posts seems valuable.

*4) We'll still be using prefixes after the change.*

While the removal of "NS" is far-reaching, prefixes will still be a common
occurrence in code written in Swift. UIKit and AppKit, along with all the
various frameworks, will still retain their prefixes, so removing prefixes
in the case of Foundation types will be more the exception than the norm.
As such, any benefit of the removal would be mitigated by the continued use
of prefixes for the rest of the first-party types we rely on.

In sum, the change would make the language and its APIs more confusing and
more difficult to use today, would make it more difficult to migrate to
modern designs in the future, and would ultimately provide a very minor
benefit. I encourage the Swift core team to reconsider the "Strip the "NS"
prefix from Foundation APIs" portion of the proposal.

Nate

On Jan 30, 2016, at 5:01 PM, Drew Crawford via swift-evolution < > swift-evolution@swift.org> wrote:

I am in favor of this proposal on balance, and I leave the bulk of this to
people who interop with Objective-C more often than I do.

I would like to confine my remarks to one corner where I think we are
making a very serious mistake.

The removal of the "NS" prefix for the Foundation module (or other
specifically identified modules) is a mechanical translation for all global
symbols defined within that module that can be performed in the Clang
importer.

As I understand it (and I am no Cocoa historian) the NS prefix was
originally introduced because Objective-C lacks namespacing.

The thinking seems to be that since Swift has proper namespacing, this
historicism is no longer necessary. I find this argument very flimsy.

Of course Swift has stronger namespacing if one's frame of reference is
Objective-C or C. But for those of us coming from other language
backgrounds, namespacing means something much stronger than Swift's concept
of it. I don't mean to suggest that Swift's design is wrong exactly (less
is sometimes more), but I do mean to say that if we propose to undo a
decision that worked for several decades and break every Swift project in
existence on the theory that Swift's namespacing is strong enough we had
better be right.

For those unfamiliar, I will explain some of the namespacing tools Swift
lacks relative to other languages. First, many languages have a
"hierarchical" namespace system, where one can say

import Foundation.Net.URL
let s = Session(...)

instead of for example

import Foundation
let s = NSURLSession(...)

Some form of this is used in Rust, Python, and C#, as far as I know. I
believe Swift has some counterfeit version of this, as the book mentions
you can import a "submodule
<The Swift Programming Language: Redirect,
but I do not know what that is, do not know how to make one, have never
seen anyone use one, the book suggests it goes only 2 levels deep, and
perhaps as a consequences of some of these problems nobody thought of using
this for Foundation.

A closely related difference is the use of so-called "selective" imports,
where we import only a single symbol (or a list of explicitly-identified
symbols) into the scope. We might express this as

from Foundation.Net.URL import Session, HTTP //import two classes only
let s = Session(...)

Again I think Swift technically supports some way to avoid importing a
whole gigantic namespace like Foundation, but I am not aware of any actual
uses of this feature, and certainly the convention is not to write code
this way. Meanwhile, let's check in with the Python community, who
standardized the following guidance on these "wildcard" imports as part of
their language evolution process:

Wildcard imports ( from <module> import * ) should be avoided, as they
make it unclear which names are present in the namespace, confusing both
readers and many automated tools. There is one defensible use case for a
wildcard import...

When a language has a robust namespacing system, which we do not, there
are many follow-on consequences. One is that an import statement is much
more of a scalpel than a bludgeon; each import statement only introduces a
handful of new names (even if it is a so-called "wildcard" import that
grabs all children of some namespace, most of those children are themselves
namespaces), unlike importing Foundation which contains thousands of direct
child types that are injected into the local scope.

Another consequence is that class names become quite short, shadow each
other, and nobody bats an eye. I searched the C# standard library for
"Session", and found some 12 classes with that name:

<Screen Shot 2016-01-30 at 3.44.23 PM.png>

These "standard library" classes not only potentially shadow
programmer-defined types, *they also shadow each other*. But because the
C# language has a robust namespacing system, the chances of there being
more than one thing called "Session" in scope in your program (or for that
matter, when developing the standard library itself) is quite small, so
it's a non-issue.

Now we return to the question of dropping the NS prefix, which will rename
thousands of classes in a typical program that has `import Foundation`, in
a way that potentially (let's be honest. More like "probably") shadows one
or more programmer-defined classes. Our review criteria is:

Is the problem being addressed significant enough to warrant a change to
Swift?

No, the elimination of 2 characters is not significant enough of a problem
to break all Swift programs, let alone to introduce literally thousands of
new opportunities for shadowing.

To its credit, the proposal acknowledges this, and offers a concession:

Note that this removal can create conflicts with the standard library. For
example, NSString and NSArray will become String and Array, respectively,
and Foundation's versions will shadow the standard library's versions. In
cases where the Swift 3 names of standard library entities conflict with
prefix-stripped Foundation entities, we retain the NS prefix. These
Foundation entities are: NSArray, NSDictionary, NSInteger, NSRange, NSSet,
and NSString.

But of course this needlessly draws a distinction between NSString et al
and the "normal" Foundation types, and what's more it draws that
distinction based on the present composition of the Swift standard library
and the present composition of Foundation. But we have already decided not
to guarantee the source compatibility of the standard library, so what
happens when that composition changes? Will we then go back and tweak
which classes get NS prefixed to them again?

In my view, if Swift's namespacing is not good enough to let us drop the
NS in NSString it is not good enough to drop any prefix. If we believe
that a programmer will struggle to distinguish between Swift String and
Foundation String then we should expect them to struggle for any two
classes in any two frameworks, and this is special pleading on the part of
Foundation. C#'s libraries declare **twelve** different `Session`s and
nobody bats an eye, but we have two types share a name and everybody loses
their minds? Our namespacing is not good enough to kill the prefix, period.

We should either drop these prefixes or we should not; because the claimed
motivation–that we have "good enough" namespacing in the language now–is
either true or it is not. This proposal admits that it is not, and tries
to drop the prefixes anyway. I believe that is a mistake.

I certainly support the goal of eliminating these prefixes, they are ugly,
they need to be killed, and namespacing is the right solution. But we must
not jump out of the plane until we are very sure our parachute is in
order. In Swift 3 it is not.

I do think the bulk of the proposal is fine, and I apologize for using
quite stark language for such a small paragraph in an otherwise reasonable
proposal, but I think the problem buried in here is quite serious and is
being overlooked.

Drew

On Jan 22, 2016, at 3:02 PM, Douglas Gregor via swift-evolution < > swift-evolution@swift.org> wrote:

Hello Swift community,

The review of SE-0005"Better Translation of Objective-C APIs Into Swift"
begins now and runs through January 31, 2016. The proposal is available
here:

https://github.com/apple/swift-evolution/blob/master/proposals/0005-objective-c-name-translation.md

Reviews are an important part of the Swift evolution process. All reviews
should be sent to the swift-evolution mailing list at

https://lists.swift.org/mailman/listinfo/swift-evolution

or, if you would like to keep your feedback private, directly to the
review manager. When replying, please try to keep the proposal link at the
top of the message:

Proposal link:

https://github.com/apple/swift-evolution/blob/master/proposals/0005-objective-c-name-translation.md

Reply text

Other replies

<GitHub - apple/swift-evolution: This maintains proposals for changes and user-visible enhancements to the Swift Programming Language.
goes into a review?

The goal of the review process is to improve the proposal under review
through constructive criticism and, eventually, determine the direction of
Swift. When writing your review, here are some questions you might want to
answer in your review:

   - What is your evaluation of the proposal?
   - Is the problem being addressed significant enough to warrant a
   change to Swift?
   - Does this proposal fit well with the feel and direction of Swift?
   - If you have used other languages or libraries with a similar
   feature, how do you feel that this proposal compares to those?
   - How much effort did you put into your review? A glance, a quick
   reading, or an in-depth study?

More information about the Swift evolution process is available at

https://github.com/apple/swift-evolution/blob/master/process.md

Thank you,

-Doug Gregor

Review Manager
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

+1 to most of what Nate says here.

I was about to reply in much more brief manner with the same general thoughts. I’m glad Nate took the time to write a more complete response. I don’t want to see a rush to retain reference types where value types are a better long-term design. The `NS` prefix gives a subtle indication that the type is not the final solution for Swift.

The only comment I want to add is that the argument is mostly specific to Foundation. In most cases removing the prefix for higher level frameworks is appropriate as the types would by reference types in Swift anyway and are not near-tern candidates for a Swifty redesign.

Perhaps the best solution is an annotation in Objective-C similar to the null annotations. In Foundation the default would be to leave the `NS` prefix (at least on classes). Everywhere else the default would be to remove the prefix. In specific cases an annotation could be used to override the default.

-Matthew

···

On Feb 1, 2016, at 3:10 PM, Nate Cook via swift-evolution <swift-evolution@swift.org> wrote:

This is a concurring opinion with Drew's review, agreeing that we should reconsider removing the "NS" prefix but providing my own reasons. The proposal as a whole is exciting and far-reaching, but this particular change is misguided. My objections are:

1) The change will elide the different semantic expectations for Foundation and Swift standard library types.

Prefix-less Foundation types will blur the different norms and expectations for Foundation types vs what we have in the Swift standard library, particularly in regards to value vs reference semantics of collection types.

The Swift standard library defines Array, Set, and Dictionary as collection types with value semantics that operate quite differently from their peers in Foundation. This change will introduce OrderedSet, CountedSet, HashTable, MapTable, and others, all of which use reference semantics and therefore don't provide the same set of guarantees about ownership and immutability.

As an example, the seemingly similar Set and CountedSet types produce different results from nearly identical code. Swift's Set type has value semantics, so changes to a copy don't affect the original Set instance:

let simpleSet = Set(["one", "two", "three"])
var copiedSimpleSet = simpleSet
copiedSimpleSet.remove("one")

copiedSimpleSet.contains("one") // false
simpleSet.contains("one") // true

CountedSet (née NSCountedSet), on the other hand, uses reference semantics, so changes to a copy of an instance. This is true whether the copy is an explicit one made via assignment or the implicit copy made when calling a function with CountedSet as a parameter. This example shows how nearly code nearly identical to the above produces very different results:

let countedSet = CountedSet(array: ["one", "two", "three"])
var copiedCountedSet = countedSet
copiedCountedSet.remove("one")

copiedCountedSet.contains("one") // false
countedSet.contains("one") // false!

Collections constructed from immutable/mutable class clusters (like OrderedSet and MutableOrderedSet) pose the same problems, since you may be using a mutable instance of the "immutable" collection. We clearly are already dealing with this difference today, but eliminating the "NS" prefix implies that Foundation types are on the same level as the standard library. This makes the confusion more pronounced and significantly increases both the learning curve of the Swift & Foundation APIs and the risk of errors when using different collection types.

2) The change may stifle the development of more Swift-oriented APIs.

Foundation types were developed in and for a language that uses reference semantics and subclassing, rather than value semantics and a protocol-oriented approach. The designs therefore use and reinforce norms that relate better to Objective-C than to Swift—class clusters of non-final, immutable types with mutable subclasses, immutable types with separate but related mutable counterparts, etc.

A Swift-native CountedSet (and other collections) would surely have value semantics built in. What about other types—do the new Calendar/Date/DateComponents types look like the system that we would design from the ground up in Swift? How about URL and URLComponents? Dropping the "NS" prefix would make it more difficult to gradually expand the standard library to encompass bridging versions of these and other common types.

3) The change will make it harder to find information about the revised APIs.

Excellent search-ability is an unintended benefit of the "NS" prefix, and can be understood as a way that these types avoid collisions in the vast namespace-less sea of Internet search results. Searching for help with URL and Calendar will be much more difficult than their NS'ed counterparts.

Especially given the challenges that this proposal will pose for code sourced from tutorials, blog posts, Stack Overflow answers, etc., keeping at least the class names as sign posts seems valuable.

4) We'll still be using prefixes after the change.

While the removal of "NS" is far-reaching, prefixes will still be a common occurrence in code written in Swift. UIKit and AppKit, along with all the various frameworks, will still retain their prefixes, so removing prefixes in the case of Foundation types will be more the exception than the norm. As such, any benefit of the removal would be mitigated by the continued use of prefixes for the rest of the first-party types we rely on.

In sum, the change would make the language and its APIs more confusing and more difficult to use today, would make it more difficult to migrate to modern designs in the future, and would ultimately provide a very minor benefit. I encourage the Swift core team to reconsider the "Strip the "NS" prefix from Foundation APIs" portion of the proposal.

Nate

On Jan 30, 2016, at 5:01 PM, Drew Crawford via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I am in favor of this proposal on balance, and I leave the bulk of this to people who interop with Objective-C more often than I do.

I would like to confine my remarks to one corner where I think we are making a very serious mistake.

The removal of the "NS" prefix for the Foundation module (or other specifically identified modules) is a mechanical translation for all global symbols defined within that module that can be performed in the Clang importer.

As I understand it (and I am no Cocoa historian) the NS prefix was originally introduced because Objective-C lacks namespacing.

The thinking seems to be that since Swift has proper namespacing, this historicism is no longer necessary. I find this argument very flimsy.

Of course Swift has stronger namespacing if one's frame of reference is Objective-C or C. But for those of us coming from other language backgrounds, namespacing means something much stronger than Swift's concept of it. I don't mean to suggest that Swift's design is wrong exactly (less is sometimes more), but I do mean to say that if we propose to undo a decision that worked for several decades and break every Swift project in existence on the theory that Swift's namespacing is strong enough we had better be right.

For those unfamiliar, I will explain some of the namespacing tools Swift lacks relative to other languages. First, many languages have a "hierarchical" namespace system, where one can say

import Foundation.Net.URL
let s = Session(...)

instead of for example

import Foundation
let s = NSURLSession(...)

Some form of this is used in Rust, Python, and C#, as far as I know. I believe Swift has some counterfeit version of this, as the book mentions you can import a "submodule <The Swift Programming Language: Redirect, but I do not know what that is, do not know how to make one, have never seen anyone use one, the book suggests it goes only 2 levels deep, and perhaps as a consequences of some of these problems nobody thought of using this for Foundation.

A closely related difference is the use of so-called "selective" imports, where we import only a single symbol (or a list of explicitly-identified symbols) into the scope. We might express this as

from Foundation.Net.URL import Session, HTTP //import two classes only
let s = Session(...)

Again I think Swift technically supports some way to avoid importing a whole gigantic namespace like Foundation, but I am not aware of any actual uses of this feature, and certainly the convention is not to write code this way. Meanwhile, let's check in with the Python community, who standardized the following guidance on these "wildcard" imports as part of their language evolution process:

Wildcard imports ( from <module> import * ) should be avoided, as they make it unclear which names are present in the namespace, confusing both readers and many automated tools. There is one defensible use case for a wildcard import...

When a language has a robust namespacing system, which we do not, there are many follow-on consequences. One is that an import statement is much more of a scalpel than a bludgeon; each import statement only introduces a handful of new names (even if it is a so-called "wildcard" import that grabs all children of some namespace, most of those children are themselves namespaces), unlike importing Foundation which contains thousands of direct child types that are injected into the local scope.

Another consequence is that class names become quite short, shadow each other, and nobody bats an eye. I searched the C# standard library for "Session", and found some 12 classes with that name:

<Screen Shot 2016-01-30 at 3.44.23 PM.png>

These "standard library" classes not only potentially shadow programmer-defined types, they also shadow each other. But because the C# language has a robust namespacing system, the chances of there being more than one thing called "Session" in scope in your program (or for that matter, when developing the standard library itself) is quite small, so it's a non-issue.

Now we return to the question of dropping the NS prefix, which will rename thousands of classes in a typical program that has `import Foundation`, in a way that potentially (let's be honest. More like "probably") shadows one or more programmer-defined classes. Our review criteria is:

Is the problem being addressed significant enough to warrant a change to Swift?

No, the elimination of 2 characters is not significant enough of a problem to break all Swift programs, let alone to introduce literally thousands of new opportunities for shadowing.

To its credit, the proposal acknowledges this, and offers a concession:

Note that this removal can create conflicts with the standard library. For example, NSString and NSArray will become String and Array, respectively, and Foundation's versions will shadow the standard library's versions. In cases where the Swift 3 names of standard library entities conflict with prefix-stripped Foundation entities, we retain the NS prefix. These Foundation entities are: NSArray, NSDictionary, NSInteger, NSRange, NSSet, and NSString.

But of course this needlessly draws a distinction between NSString et al and the "normal" Foundation types, and what's more it draws that distinction based on the present composition of the Swift standard library and the present composition of Foundation. But we have already decided not to guarantee the source compatibility of the standard library, so what happens when that composition changes? Will we then go back and tweak which classes get NS prefixed to them again?

In my view, if Swift's namespacing is not good enough to let us drop the NS in NSString it is not good enough to drop any prefix. If we believe that a programmer will struggle to distinguish between Swift String and Foundation String then we should expect them to struggle for any two classes in any two frameworks, and this is special pleading on the part of Foundation. C#'s libraries declare *twelve* different `Session`s and nobody bats an eye, but we have two types share a name and everybody loses their minds? Our namespacing is not good enough to kill the prefix, period.

We should either drop these prefixes or we should not; because the claimed motivation–that we have "good enough" namespacing in the language now–is either true or it is not. This proposal admits that it is not, and tries to drop the prefixes anyway. I believe that is a mistake.

I certainly support the goal of eliminating these prefixes, they are ugly, they need to be killed, and namespacing is the right solution. But we must not jump out of the plane until we are very sure our parachute is in order. In Swift 3 it is not.

I do think the bulk of the proposal is fine, and I apologize for using quite stark language for such a small paragraph in an otherwise reasonable proposal, but I think the problem buried in here is quite serious and is being overlooked.

Drew

On Jan 22, 2016, at 3:02 PM, Douglas Gregor via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hello Swift community,

The review of SE-0005"Better Translation of Objective-C APIs Into Swift" begins now and runs through January 31, 2016. The proposal is available here:

https://github.com/apple/swift-evolution/blob/master/proposals/0005-objective-c-name-translation.md
Reviews are an important part of the Swift evolution process. All reviews should be sent to the swift-evolution mailing list at

https://lists.swift.org/mailman/listinfo/swift-evolution
or, if you would like to keep your feedback private, directly to the review manager. When replying, please try to keep the proposal link at the top of the message:

Proposal link:

https://github.com/apple/swift-evolution/blob/master/proposals/0005-objective-c-name-translation.md
Reply text

Other replies
<GitHub - apple/swift-evolution: This maintains proposals for changes and user-visible enhancements to the Swift Programming Language. goes into a review?

The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift. When writing your review, here are some questions you might want to answer in your review:

What is your evaluation of the proposal?
Is the problem being addressed significant enough to warrant a change to Swift?
Does this proposal fit well with the feel and direction of Swift?
If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
How much effort did you put into your review? A glance, a quick reading, or an in-depth study?
More information about the Swift evolution process is available at

https://github.com/apple/swift-evolution/blob/master/process.md
Thank you,

-Doug Gregor

Review Manager

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I think the "canonical URL for everyone to use" must be a value type / struct. To me this seems axiomatic.

Meanwhile (NS)URL cannot be a value type because of the Darwin compatibility mandate, and to change from value->reference semantics would be "off the charts" breakage severity.

This is a requirements conflict. corelibs-foundation can either be Darwin-compatible or it can be canonical, but we cannot do both. Right now we have chosen Darwin-compatible, so we must let the canonical requirement go. Unprefixed "URL" should be reserved for the value type that can actually work as a canonical API.

It's not clear to me if Foundation will actually be in a position to provide that API (e.g. in Swift 4, etc.) or whether some third party will provide a "URL" framework that gains traction first. But in any case, we are not going to do it in the Swift 3 window, unless a lot of core scope decisions get re-decided.

···

On Feb 1, 2016, at 1:02 PM, Tony Parker <anthony.parker@apple.com> wrote:

Types like NSURL are intended to be the canonical URL for everyone to use,

Hi Drew,

I am in favor of this proposal on balance, and I leave the bulk of
this to people who interop with Objective-C more often than I do.

I would like to confine my remarks to one corner where I think we
are making a very serious mistake.

The removal of the "NS" prefix for the Foundation module (or other
specifically identified modules) is a mechanical translation for
all global symbols defined within that module that can be performed
in the Clang importer.

As I understand it (and I am no Cocoa historian) the NS prefix was
originally introduced because Objective-C lacks namespacing.

The thinking seems to be that since Swift has proper namespacing,
this historicism is no longer necessary. I find this argument very
flimsy.

Of course Swift has stronger namespacing if one's frame of reference
is Objective-C or C. But for those of us coming from other language
backgrounds, namespacing means something much stronger than Swift's
concept of it. I don't mean to suggest that Swift's design is wrong
exactly (less is sometimes more), but I do mean to say that if we
propose to undo a decision that worked for several decades and break
every Swift project in existence on the theory that Swift's
namespacing is strong enough we had better be right.

For those unfamiliar, I will explain some of the namespacing tools
Swift lacks relative to other languages. First, many languages have
a "hierarchical" namespace system, where one can say

import Foundation.Net.URL
let s = Session(...)

instead of for example

import Foundation
let s = NSURLSession(...)

Some form of this is used in Rust, Python, and C#, as far as I know.
I believe Swift has some counterfeit version of this, as the book
mentions you can import a "submodule
<The Swift Programming Language: Redirect,
but I do not know what that is, do not know how to make one, have
never seen anyone use one, the book suggests it goes only 2 levels
deep, and perhaps as a consequences of some of these problems nobody
thought of using this for Foundation.

A closely related difference is the use of so-called "selective"
imports, where we import only a single symbol (or a list of
explicitly-identified symbols) into the scope. We might express
this as

from Foundation.Net.URL import Session, HTTP //import two classes only
let s = Session(...)

Again I think Swift technically supports some way to avoid importing
a whole gigantic namespace like Foundation, but I am not aware of
any actual uses of this feature, and certainly the convention is not
to write code this way. Meanwhile, let's check in with the Python
community, who standardized the following guidance on these
"wildcard" imports as part of their language evolution process:

Wildcard imports ( from <module> import * ) should be avoided, as
they make it unclear which names are present in the namespace,
confusing both readers and many automated tools. There is one
defensible use case for a wildcard import...

When a language has a robust namespacing system, which we do not,
there are many follow-on consequences. One is that an import
statement is much more of a scalpel than a bludgeon; each import
statement only introduces a handful of new names (even if it is a
so-called "wildcard" import that grabs all children of some
namespace, most of those children are themselves namespaces), unlike
importing Foundation which contains thousands of direct child types
that are injected into the local scope.

Another consequence is that class names become quite short, shadow
each other, and nobody bats an eye. I searched the C# standard
library for "Session", and found some 12 classes with that name:

<Screen Shot 2016-01-30 at 3.44.23 PM.png>

These "standard library" classes not only potentially shadow
programmer-defined types, they also shadow each other. But because
the C# language has a robust namespacing system, the chances of
there being more than one thing called "Session" in scope in your
program (or for that matter, when developing the standard library
itself) is quite small, so it's a non-issue.

Now we return to the question of dropping the NS prefix, which will
rename thousands of classes in a typical program that has `import
Foundation`, in a way that potentially (let's be honest. More like
"probably") shadows one or more programmer-defined classes. Our
review criteria is:

Is the problem being addressed significant enough to warrant a change to Swift?

No, the elimination of 2 characters is not significant enough of a
problem to break all Swift programs, let alone to introduce
literally thousands of new opportunities for shadowing.

To its credit, the proposal acknowledges this, and offers a concession:

Note that this removal can create conflicts with the standard
library. For example, NSString and NSArray will become String and
Array, respectively, and Foundation's versions will shadow the
standard library's versions. In cases where the Swift 3 names of
standard library entities conflict with prefix-stripped Foundation
entities, we retain the NS prefix. These Foundation entities are:
NSArray, NSDictionary, NSInteger, NSRange, NSSet, and NSString.

But of course this needlessly draws a distinction between NSString
et al and the "normal" Foundation types, and what's more it draws
that distinction based on the present composition of the Swift
standard library and the present composition of Foundation. But we
have already decided not to guarantee the source compatibility of
the standard library, so what happens when that composition changes?
Will we then go back and tweak which classes get NS prefixed to them
again?

In my view, if Swift's namespacing is not good enough to let us drop
the NS in NSString it is not good enough to drop any prefix. If we
believe that a programmer will struggle to distinguish between Swift
String and Foundation String then we should expect them to struggle
for any two classes in any two frameworks, and this is special
pleading on the part of Foundation. C#'s libraries declare *twelve*
different `Session`s and nobody bats an eye, but we have two types
share a name and everybody loses their minds? Our namespacing is
not good enough to kill the prefix, period.

I’m actually not sure how this line about shadowing ended up in the
guidelines,

Tony, what do you mean? Unless I missed something, nobody is claiming
that the guidelines say anything directly about shadowing.

···

on Mon Feb 01 2016, Tony Parker <swift-evolution@swift.org> wrote:

On Jan 30, 2016, at 3:01 PM, Drew Crawford via swift-evolution >> <swift-evolution@swift.org> wrote:

because it is not our plan. Instead, I have always wanted to do
something very close to what you suggest and rename these to something
like “Dynamic.Array” to reflect its role as a dynamically-dispatched,
subclass-capable Array.

Types like NSURL are intended to be the canonical URL for everyone to
use, so for me it feels very natural to drop the prefix and make them
as accessible as Array, String, etc.

- Tony

We should either drop these prefixes or we should not; because the
claimed motivation–that we have "good enough" namespacing in the
language now–is either true or it is not. This proposal admits that
it is not, and tries to drop the prefixes anyway. I believe that is
a mistake.

I certainly support the goal of eliminating these prefixes, they are
ugly, they need to be killed, and namespacing is the right solution.
But we must not jump out of the plane until we are very sure our
parachute is in order. In Swift 3 it is not.

I do think the bulk of the proposal is fine, and I apologize for
using quite stark language for such a small paragraph in an
otherwise reasonable proposal, but I think the problem buried in
here is quite serious and is being overlooked.

Drew

On Jan 22, 2016, at 3:02 PM, Douglas Gregor via swift-evolution >>> <swift-evolution@swift.org >>> <mailto:swift-evolution@swift.org>> >>> wrote:

Hello Swift community,

The review of SE-0005"Better Translation of Objective-C APIs Into
Swift" begins now and runs through January 31, 2016. The proposal
is available here:

https://github.com/apple/swift-evolution/blob/master/proposals/0005-objective-c-name-translation.md
<https://github.com/apple/swift-evolution/blob/master/proposals/0005-objective-c-name-translation.md&gt;
Reviews are an important part of the Swift evolution process. All
reviews should be sent to the swift-evolution mailing list at

https://lists.swift.org/mailman/listinfo/swift-evolution
<https://lists.swift.org/mailman/listinfo/swift-evolution&gt;
or, if you would like to keep your feedback private, directly to
the review manager. When replying, please try to keep the proposal
link at the top of the message:

Proposal link:

https://github.com/apple/swift-evolution/blob/master/proposals/0005-objective-c-name-translation.md
<https://github.com/apple/swift-evolution/blob/master/proposals/0005-objective-c-name-translation.md&gt;
Reply text

Other replies
<GitHub - apple/swift-evolution: This maintains proposals for changes and user-visible enhancements to the Swift Programming Language.
goes into a review?

The goal of the review process is to improve the proposal under
review through constructive criticism and, eventually, determine
the direction of Swift. When writing your review, here are some
questions you might want to answer in your review:

What is your evaluation of the proposal?
Is the problem being addressed significant enough to warrant a change to Swift?
Does this proposal fit well with the feel and direction of Swift?
If you have used other languages or libraries with a similar
feature, how do you feel that this proposal compares to those?
How much effort did you put into your review? A glance, a quick
reading, or an in-depth study?
More information about the Swift evolution process is available at

https://github.com/apple/swift-evolution/blob/master/process.md
<https://github.com/apple/swift-evolution/blob/master/process.md&gt;
Thank you,

-Doug Gregor

Review Manager

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
<mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

--
-Dave

I’d like to voice my opinion before is too late and reiterate what Nate said. Although I’m not going to add much, because Nate already said it all better than I could.

• What is your evaluation of the proposal?

I agree with most of the proposal except for "Strip the "NS" prefix from Foundation APIs”. I strongly disagree with this change for the same reasons as Nate:

- This change will make Foundation types look Swift native enough to confuse some users between the stdlib’s value semantics and Foundation’s reference semantics.
- The change will complicate the creation of a Swift native foundation that uses the power of Swift to provide true value semantics to types that need them (URL, Data, CountedSet, etc…)

• Is the problem being addressed significant enough to warrant a change to Swift?

I don’t necessarily think that the problem being addressed is that significant, but the change is appreciated (except for the NS prefix). To be honest, I would have preferred more design and development effort to go towards enhancing the Swift language itself, or the emergence of a real Swift foundation framework.

• Does this proposal fit well with the feel and direction of Swift?

It does.

• If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

Nothing to say here.

• How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

I have kept up with the discussion since the start and have tried to keep an open mind about everybody’s opinion.

David.

···

On 01 Feb 2016, at 22:10, Nate Cook via swift-evolution <swift-evolution@swift.org> wrote:

This is a concurring opinion with Drew's review, agreeing that we should reconsider removing the "NS" prefix but providing my own reasons. The proposal as a whole is exciting and far-reaching, but this particular change is misguided. My objections are:

1) The change will elide the different semantic expectations for Foundation and Swift standard library types.

Prefix-less Foundation types will blur the different norms and expectations for Foundation types vs what we have in the Swift standard library, particularly in regards to value vs reference semantics of collection types.

The Swift standard library defines Array, Set, and Dictionary as collection types with value semantics that operate quite differently from their peers in Foundation. This change will introduce OrderedSet, CountedSet, HashTable, MapTable, and others, all of which use reference semantics and therefore don't provide the same set of guarantees about ownership and immutability.

As an example, the seemingly similar Set and CountedSet types produce different results from nearly identical code. Swift's Set type has value semantics, so changes to a copy don't affect the original Set instance:

let simpleSet = Set(["one", "two", "three"])
var copiedSimpleSet = simpleSet
copiedSimpleSet.remove("one")

copiedSimpleSet.contains("one") // false
simpleSet.contains("one") // true

CountedSet (née NSCountedSet), on the other hand, uses reference semantics, so changes to a copy of an instance. This is true whether the copy is an explicit one made via assignment or the implicit copy made when calling a function with CountedSet as a parameter. This example shows how nearly code nearly identical to the above produces very different results:

let countedSet = CountedSet(array: ["one", "two", "three"])
var copiedCountedSet = countedSet
copiedCountedSet.remove("one")

copiedCountedSet.contains("one") // false
countedSet.contains("one") // false!

Collections constructed from immutable/mutable class clusters (like OrderedSet and MutableOrderedSet) pose the same problems, since you may be using a mutable instance of the "immutable" collection. We clearly are already dealing with this difference today, but eliminating the "NS" prefix implies that Foundation types are on the same level as the standard library. This makes the confusion more pronounced and significantly increases both the learning curve of the Swift & Foundation APIs and the risk of errors when using different collection types.

2) The change may stifle the development of more Swift-oriented APIs.

Foundation types were developed in and for a language that uses reference semantics and subclassing, rather than value semantics and a protocol-oriented approach. The designs therefore use and reinforce norms that relate better to Objective-C than to Swift—class clusters of non-final, immutable types with mutable subclasses, immutable types with separate but related mutable counterparts, etc.

A Swift-native CountedSet (and other collections) would surely have value semantics built in. What about other types—do the new Calendar/Date/DateComponents types look like the system that we would design from the ground up in Swift? How about URL and URLComponents? Dropping the "NS" prefix would make it more difficult to gradually expand the standard library to encompass bridging versions of these and other common types.

3) The change will make it harder to find information about the revised APIs.

Excellent search-ability is an unintended benefit of the "NS" prefix, and can be understood as a way that these types avoid collisions in the vast namespace-less sea of Internet search results. Searching for help with URL and Calendar will be much more difficult than their NS'ed counterparts.

Especially given the challenges that this proposal will pose for code sourced from tutorials, blog posts, Stack Overflow answers, etc., keeping at least the class names as sign posts seems valuable.

4) We'll still be using prefixes after the change.

While the removal of "NS" is far-reaching, prefixes will still be a common occurrence in code written in Swift. UIKit and AppKit, along with all the various frameworks, will still retain their prefixes, so removing prefixes in the case of Foundation types will be more the exception than the norm. As such, any benefit of the removal would be mitigated by the continued use of prefixes for the rest of the first-party types we rely on.

In sum, the change would make the language and its APIs more confusing and more difficult to use today, would make it more difficult to migrate to modern designs in the future, and would ultimately provide a very minor benefit. I encourage the Swift core team to reconsider the "Strip the "NS" prefix from Foundation APIs" portion of the proposal.

Nate

On Jan 30, 2016, at 5:01 PM, Drew Crawford via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I am in favor of this proposal on balance, and I leave the bulk of this to people who interop with Objective-C more often than I do.

I would like to confine my remarks to one corner where I think we are making a very serious mistake.

The removal of the "NS" prefix for the Foundation module (or other specifically identified modules) is a mechanical translation for all global symbols defined within that module that can be performed in the Clang importer.

As I understand it (and I am no Cocoa historian) the NS prefix was originally introduced because Objective-C lacks namespacing.

The thinking seems to be that since Swift has proper namespacing, this historicism is no longer necessary. I find this argument very flimsy.

Of course Swift has stronger namespacing if one's frame of reference is Objective-C or C. But for those of us coming from other language backgrounds, namespacing means something much stronger than Swift's concept of it. I don't mean to suggest that Swift's design is wrong exactly (less is sometimes more), but I do mean to say that if we propose to undo a decision that worked for several decades and break every Swift project in existence on the theory that Swift's namespacing is strong enough we had better be right.

For those unfamiliar, I will explain some of the namespacing tools Swift lacks relative to other languages. First, many languages have a "hierarchical" namespace system, where one can say

import Foundation.Net.URL
let s = Session(...)

instead of for example

import Foundation
let s = NSURLSession(...)

Some form of this is used in Rust, Python, and C#, as far as I know. I believe Swift has some counterfeit version of this, as the book mentions you can import a "submodule <The Swift Programming Language: Redirect, but I do not know what that is, do not know how to make one, have never seen anyone use one, the book suggests it goes only 2 levels deep, and perhaps as a consequences of some of these problems nobody thought of using this for Foundation.

A closely related difference is the use of so-called "selective" imports, where we import only a single symbol (or a list of explicitly-identified symbols) into the scope. We might express this as

from Foundation.Net.URL import Session, HTTP //import two classes only
let s = Session(...)

Again I think Swift technically supports some way to avoid importing a whole gigantic namespace like Foundation, but I am not aware of any actual uses of this feature, and certainly the convention is not to write code this way. Meanwhile, let's check in with the Python community, who standardized the following guidance on these "wildcard" imports as part of their language evolution process:

Wildcard imports ( from <module> import * ) should be avoided, as they make it unclear which names are present in the namespace, confusing both readers and many automated tools. There is one defensible use case for a wildcard import...

When a language has a robust namespacing system, which we do not, there are many follow-on consequences. One is that an import statement is much more of a scalpel than a bludgeon; each import statement only introduces a handful of new names (even if it is a so-called "wildcard" import that grabs all children of some namespace, most of those children are themselves namespaces), unlike importing Foundation which contains thousands of direct child types that are injected into the local scope.

Another consequence is that class names become quite short, shadow each other, and nobody bats an eye. I searched the C# standard library for "Session", and found some 12 classes with that name:

<Screen Shot 2016-01-30 at 3.44.23 PM.png>

These "standard library" classes not only potentially shadow programmer-defined types, they also shadow each other. But because the C# language has a robust namespacing system, the chances of there being more than one thing called "Session" in scope in your program (or for that matter, when developing the standard library itself) is quite small, so it's a non-issue.

Now we return to the question of dropping the NS prefix, which will rename thousands of classes in a typical program that has `import Foundation`, in a way that potentially (let's be honest. More like "probably") shadows one or more programmer-defined classes. Our review criteria is:

Is the problem being addressed significant enough to warrant a change to Swift?

No, the elimination of 2 characters is not significant enough of a problem to break all Swift programs, let alone to introduce literally thousands of new opportunities for shadowing.

To its credit, the proposal acknowledges this, and offers a concession:

Note that this removal can create conflicts with the standard library. For example, NSString and NSArray will become String and Array, respectively, and Foundation's versions will shadow the standard library's versions. In cases where the Swift 3 names of standard library entities conflict with prefix-stripped Foundation entities, we retain the NS prefix. These Foundation entities are: NSArray, NSDictionary, NSInteger, NSRange, NSSet, and NSString.

But of course this needlessly draws a distinction between NSString et al and the "normal" Foundation types, and what's more it draws that distinction based on the present composition of the Swift standard library and the present composition of Foundation. But we have already decided not to guarantee the source compatibility of the standard library, so what happens when that composition changes? Will we then go back and tweak which classes get NS prefixed to them again?

In my view, if Swift's namespacing is not good enough to let us drop the NS in NSString it is not good enough to drop any prefix. If we believe that a programmer will struggle to distinguish between Swift String and Foundation String then we should expect them to struggle for any two classes in any two frameworks, and this is special pleading on the part of Foundation. C#'s libraries declare *twelve* different `Session`s and nobody bats an eye, but we have two types share a name and everybody loses their minds? Our namespacing is not good enough to kill the prefix, period.

We should either drop these prefixes or we should not; because the claimed motivation–that we have "good enough" namespacing in the language now–is either true or it is not. This proposal admits that it is not, and tries to drop the prefixes anyway. I believe that is a mistake.

I certainly support the goal of eliminating these prefixes, they are ugly, they need to be killed, and namespacing is the right solution. But we must not jump out of the plane until we are very sure our parachute is in order. In Swift 3 it is not.

I do think the bulk of the proposal is fine, and I apologize for using quite stark language for such a small paragraph in an otherwise reasonable proposal, but I think the problem buried in here is quite serious and is being overlooked.

Drew

On Jan 22, 2016, at 3:02 PM, Douglas Gregor via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hello Swift community,

The review of SE-0005"Better Translation of Objective-C APIs Into Swift" begins now and runs through January 31, 2016. The proposal is available here:

https://github.com/apple/swift-evolution/blob/master/proposals/0005-objective-c-name-translation.md
Reviews are an important part of the Swift evolution process. All reviews should be sent to the swift-evolution mailing list at

https://lists.swift.org/mailman/listinfo/swift-evolution
or, if you would like to keep your feedback private, directly to the review manager. When replying, please try to keep the proposal link at the top of the message:

Proposal link:

https://github.com/apple/swift-evolution/blob/master/proposals/0005-objective-c-name-translation.md
Reply text

Other replies
<GitHub - apple/swift-evolution: This maintains proposals for changes and user-visible enhancements to the Swift Programming Language. goes into a review?

The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift. When writing your review, here are some questions you might want to answer in your review:

What is your evaluation of the proposal?
Is the problem being addressed significant enough to warrant a change to Swift?
Does this proposal fit well with the feel and direction of Swift?
If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
How much effort did you put into your review? A glance, a quick reading, or an in-depth study?
More information about the Swift evolution process is available at

https://github.com/apple/swift-evolution/blob/master/process.md
Thank you,

-Doug Gregor

Review Manager

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Just be aware that comments are sometimes important for verifying that
the meaning of the API has been preserved by the rename.

Another thing that might be useful for some is to examine the diffs
between the swift-2 and swift-3 branches. For that, you might want to
do the same to your swift-2 branch.

···

on Sat Jan 23 2016, Jacob Bandes-Storch <jtbandes-AT-gmail.com> wrote:

Proposal link:
https://github.com/apple/swift-evolution/blob/master/proposals/0005-objective-c-name-translation.md

On Sat, Jan 23, 2016 at 3:37 AM, Dave Abrahams via swift-evolution < > swift-evolution@swift.org> wrote:

That information is all right here, under Platforms:

  GitHub - apple/swift-3-api-guidelines-review

HTH,

--
-Dave

Thank you, I missed that. This is great.

For easier browsing, I've removed comments and @available modifiers from
the generated interfaces:

https://github.com/jtbandes/swift-3-api-guidelines-review/tree/swift-3/Platforms

--
-Dave

Doug,

I think this change looks great! I don’t have time to look through
the full patch but did look through quite a bit. It adds clarity in
the vast majority of cases I looked at.

It seems like with-as-separator is a good heuristic for determining
when the first parameter is not essential to a good name for the
fundamental operation. I agree with the comments earlier on that in
these cases a label for the first parameter is the best approach.

I also really like that this groups methods with the same fundamental
operation into overload families where they previously had independent
names. This is a big win IMO.

There is a first-parameter-is-an-ID pattern I noticed after this
change. I show a few examples here, but there are a lot more:

- func trackWithTrackID(trackID: CMPersistentTrackID) -> AVAssetTrack?
+ func track(trackID trackID: CMPersistentTrackID) -> AVAssetTrack?

- func trackWithTrackID(trackID: CMPersistentTrackID) -> AVFragmentedAssetTrack?
+ func track(trackID trackID: CMPersistentTrackID) -> AVFragmentedAssetTrack?

- func trackWithTrackID(trackID: CMPersistentTrackID) -> AVCompositionTrack?
+ func track(trackID trackID: CMPersistentTrackID) -> AVCompositionTrack?

- func discoverUserInfoWithUserRecordID(userRecordID: CKRecordID,
completionHandler: (CKDiscoveredUserInfo?, Error?) -> Void)

+ func discoverUserInfo(userRecordID userRecordID: CKRecordID,
completionHandler: (CKDiscoveredUserInfo?, Error?) -> Void)

The first argument label `trackID` seems like it repeats type
information without adding clarity. I think it would be better to
just use `id` here. It seems like a candidate for heuristics as well.
For example, if the type name ends in ID and the label is a suffix of
the type name we could just use `id`. This is a somewhat specific
pattern, but IDs are common enough that it might make sense.

Actually I've been saying for a while that arguments called ID,
identifier, and name should not be labelled at all in many cases. Think
about it.

···

on Wed Jan 27 2016, Matthew Johnson <swift-evolution@swift.org> wrote:

Interestingly, in at least one case the `WithID` was the original name
of the method so we did receive a simple `id` label:

- func parameterWithID(paramID: AudioUnitParameterID, scope:
AudioUnitScope, element: AudioUnitElement) -> AUParameter?
+ func parameter(id paramID: AudioUnitParameterID, scope:
AudioUnitScope, element: AudioUnitElement) -> AUParameter?

In another case, the method has a naked `With` at the end. Somehow
`id` was used in that scenario despite the parameter name being
`objectID` and the type being `NSManagedObjectID`, which aligns with
my suggested naming:

- func newValuesForObjectWith(objectID: NSManagedObjectID, withContext
context: NSManagedObjectContext) throws -> NSIncrementalStoreNode
+ func newValuesForObject(id objectID: NSManagedObjectID, withContext
context: NSManagedObjectContext) throws -> NSIncrementalStoreNode

A case related to that used `iDs` for array arguments:

- func managedObjectContextDidRegisterObjectsWithIDs(objectIDs: [NSManagedObjectID])
- func managedObjectContextDidUnregisterObjectsWithIDs(objectIDs: [NSManagedObjectID])
+ func managedObjectContextDidRegisterObjects(iDs objectIDs: [NSManagedObjectID])
+ func managedObjectContextDidUnregisterObjects(iDs objectIDs: [NSManagedObjectID])

I would prefer `ids` here. This seems like a pattern that would be a
problem for any all-caps plural acronym or initialism so it might be
good to add a heuristic for this as well.

Here’s another interesting change:

- func unionWith(s2: CIFilterShape) -> CIFilterShape
- func unionWith(r: CGRect) -> CIFilterShape
- func intersectWith(s2: CIFilterShape) -> CIFilterShape
- func intersectWith(r: CGRect) -> CIFilterShape
+ func union(with s2: CIFilterShape) -> CIFilterShape
+ func union(rect r: CGRect) -> CIFilterShape
+ func intersect(with s2: CIFilterShape) -> CIFilterShape
+ func intersect(rect r: CGRect) -> CIFilterShape

Why do the CGRect arguments receive a type-derived label but the
CIFilterShape arguments just receive `with`? Shouldn’t these follow
the same pattern?

-Matthew

On Jan 27, 2016, at 1:50 AM, Douglas Gregor via swift-evolution >> <swift-evolution@swift.org> wrote:

On Jan 25, 2016, at 6:55 AM, Radosław Pietruszewski >>> <radexpl@gmail.com >>> <mailto:radexpl@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:

  Split method base name at the last "with" preposition. by DougGregor · Pull Request #5 · apple/swift-3-api-guidelines-review · GitHub
<Split method base name at the last "with" preposition. by DougGregor · Pull Request #5 · apple/swift-3-api-guidelines-review · GitHub;

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
<Sign in to GitHub · GitHub;

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/&gt;&quot;\)
    service.resource(absoluteURL: NSURL(string: "http://bar.com <http://bar.com/&gt;&quot;\))

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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

--
-Dave

Doug,

I think this change looks great! I don’t have time to look through
the full patch but did look through quite a bit. It adds clarity in
the vast majority of cases I looked at.

It seems like with-as-separator is a good heuristic for determining
when the first parameter is not essential to a good name for the
fundamental operation. I agree with the comments earlier on that in
these cases a label for the first parameter is the best approach.

I also really like that this groups methods with the same fundamental
operation into overload families where they previously had independent
names. This is a big win IMO.

There is a first-parameter-is-an-ID pattern I noticed after this
change. I show a few examples here, but there are a lot more:

- func trackWithTrackID(trackID: CMPersistentTrackID) -> AVAssetTrack?
+ func track(trackID trackID: CMPersistentTrackID) -> AVAssetTrack?

- func trackWithTrackID(trackID: CMPersistentTrackID) -> AVFragmentedAssetTrack?
+ func track(trackID trackID: CMPersistentTrackID) -> AVFragmentedAssetTrack?

- func trackWithTrackID(trackID: CMPersistentTrackID) -> AVCompositionTrack?
+ func track(trackID trackID: CMPersistentTrackID) -> AVCompositionTrack?

- func discoverUserInfoWithUserRecordID(userRecordID: CKRecordID,
completionHandler: (CKDiscoveredUserInfo?, Error?) -> Void)

+ func discoverUserInfo(userRecordID userRecordID: CKRecordID,
completionHandler: (CKDiscoveredUserInfo?, Error?) -> Void)

The first argument label `trackID` seems like it repeats type
information without adding clarity. I think it would be better to
just use `id` here. It seems like a candidate for heuristics as well.
For example, if the type name ends in ID and the label is a suffix of
the type name we could just use `id`. This is a somewhat specific
pattern, but IDs are common enough that it might make sense.

Actually I've been saying for a while that arguments called ID,
identifier, and name should not be labelled at all in many cases. Think
about it.

Looking at this closer, I agree in the single argument case. However, in cases where there are other closely related arguments I think it feels odd to not label the id parameter. Regardless, I think the ID case deserves special consideration if we go with Doug’s strategy of using `With` as a separator.

···

On Jan 27, 2016, at 12:03 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:
on Wed Jan 27 2016, Matthew Johnson <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Interestingly, in at least one case the `WithID` was the original name
of the method so we did receive a simple `id` label:

- func parameterWithID(paramID: AudioUnitParameterID, scope:
AudioUnitScope, element: AudioUnitElement) -> AUParameter?
+ func parameter(id paramID: AudioUnitParameterID, scope:
AudioUnitScope, element: AudioUnitElement) -> AUParameter?

In another case, the method has a naked `With` at the end. Somehow
`id` was used in that scenario despite the parameter name being
`objectID` and the type being `NSManagedObjectID`, which aligns with
my suggested naming:

- func newValuesForObjectWith(objectID: NSManagedObjectID, withContext
context: NSManagedObjectContext) throws -> NSIncrementalStoreNode
+ func newValuesForObject(id objectID: NSManagedObjectID, withContext
context: NSManagedObjectContext) throws -> NSIncrementalStoreNode

A case related to that used `iDs` for array arguments:

- func managedObjectContextDidRegisterObjectsWithIDs(objectIDs: [NSManagedObjectID])
- func managedObjectContextDidUnregisterObjectsWithIDs(objectIDs: [NSManagedObjectID])
+ func managedObjectContextDidRegisterObjects(iDs objectIDs: [NSManagedObjectID])
+ func managedObjectContextDidUnregisterObjects(iDs objectIDs: [NSManagedObjectID])

I would prefer `ids` here. This seems like a pattern that would be a
problem for any all-caps plural acronym or initialism so it might be
good to add a heuristic for this as well.

Here’s another interesting change:

- func unionWith(s2: CIFilterShape) -> CIFilterShape
- func unionWith(r: CGRect) -> CIFilterShape
- func intersectWith(s2: CIFilterShape) -> CIFilterShape
- func intersectWith(r: CGRect) -> CIFilterShape
+ func union(with s2: CIFilterShape) -> CIFilterShape
+ func union(rect r: CGRect) -> CIFilterShape
+ func intersect(with s2: CIFilterShape) -> CIFilterShape
+ func intersect(rect r: CGRect) -> CIFilterShape

Why do the CGRect arguments receive a type-derived label but the
CIFilterShape arguments just receive `with`? Shouldn’t these follow
the same pattern?

-Matthew

On Jan 27, 2016, at 1:50 AM, Douglas Gregor via swift-evolution >>> <swift-evolution@swift.org> wrote:

On Jan 25, 2016, at 6:55 AM, Radosław Pietruszewski >>>> <radexpl@gmail.com >>>> <mailto:radexpl@gmail.com <mailto:radexpl@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:

  Split method base name at the last "with" preposition. by DougGregor · Pull Request #5 · apple/swift-3-api-guidelines-review · GitHub
<Split method base name at the last "with" preposition. by DougGregor · Pull Request #5 · apple/swift-3-api-guidelines-review · GitHub;

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
<Sign in to GitHub · GitHub;

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/&gt; <http://bar.com/&gt;&quot;\)
   service.resource(absoluteURL: NSURL(string: "http://bar.com <http://bar.com/&gt; <http://bar.com/&gt;&quot;\))

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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

--
-Dave

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

Doug,

For methods such as:

func description(locale locale: AnyObject?) -> String
func description(locale locale: AnyObject?, indent level: Int) -> String

1. Can the `locale` parameter be given a default `= nil` argument?

2. Can the parameter type be changed to `NSLocale?` when translated into Swift?

The Objective-C version takes either an NSDictionary or an NSLocale. I think we could consider it making NSLocale-only in Swift.

- Tony

···

On Jan 27, 2016, at 1:07 PM, Ben Rimmington via swift-evolution <swift-evolution@swift.org> wrote:

-- Ben
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Doug,

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).

The related AppKit.NSBezierPath method contains two possible "With" separators:

- (void)appendBezierPathWithArcWithCenter:(NSPoint)center
                                  radius:(CGFloat)radius
                              startAngle:(CGFloat)startAngle
                                endAngle:(CGFloat)endAngle
                               clockwise:(BOOL)clockwise

Your patch replaces the second "With" separator, but the alternative might be better:

// YOUR PATCH:
func appendWithArc(center center: Point, radius: CGFloat, startAngle: CGFloat, endAngle: CGFloat, clockwise: Bool)

// THE ALTERNATIVE:
func append(arcWithCenter center: Point, radius: CGFloat, startAngle: CGFloat, endAngle: CGFloat, clockwise: Bool)

This is better IMO:

func appendArc(center center: Point, radius: CGFloat, startAngle: CGFloat, endAngle: CGFloat, clockwise: Bool)

However, I think this is an example of where explicit annotation is probably necessary to get the best result. A heuristic can’t always do the right thing and I think this example is past the point of diminishing returns for heuristics.

-Matthew

···

On Jan 27, 2016, at 1:22 PM, Ben Rimmington via swift-evolution <swift-evolution@swift.org> wrote:

On 27 Jan 2016, at 07:50, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:

-- Ben
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

This is great! Thank you Doug for taking a look at this.

This is a huge patch, so I haven’t looked through it all, but a few interesting bits:

- func finishWritingWithCompletionHandler(handler: () -> Void)
+ func finishWriting(completionHandler handler: () -> Void)

This is something I mentioned in my review of SE-0023. An example with a completionHandler param was made in the Guidelines (that should have an explicit label because it has a default value), and I argued that it would make sense for the “completionHandler” to have a label _even if it didn’t have a default value_. A few examples in the diff seem to confirm this notion for me.

Ah, good point. “WithCompletionHandler” is a well-followed convention, so I’d expect to see more

- func respondWith(data: Data)
+ func respond(data data: Data)

This one is unusual. I don’t mind `respond(data: …)`, but generally there’s a word to the right of “with”, and here the “with” was just to imply the argument without naming it.

The original ObjC method name was “respondWithData:”. “Data” restates type info, so SE-0023 stripped it off; this transformation makes it the first argument label.

- class func instantiateWith(audioComponentDescription: AudioComponentDescription, options: AudioComponentInstantiationOptions = , completionHandler: (AVAudioUnit?, Error?) -> Void)
+ class func instantiate(componentDescription audioComponentDescription: AudioComponentDescription, options: AudioComponentInstantiationOptions = , completionHandler: (AVAudioUnit?, Error?) -> Void)

Another unusual one. I don’t know AV* APIs, but for whatever reason they have an “instantiate” class method instead of just init. But just like with init the first param name is moved from the “initWithFoo” name to an label, here it also seems to make a lot of sense.

Note that the instantiated audio unit is provided to a completion handler, so this can’t be an initializer.

- func indexOfItemWithTitle(title: String) -> Int
- func indexOfItemWithTag(tag: Int) -> Int
- func indexOfItemWithRepresentedObject(obj: AnyObject?) -> Int
- func indexOfItemWithTarget(target: AnyObject?, andAction actionSelector: Selector) -> Int
+ func indexOfItem(title title: String) -> Int
+ func indexOfItem(tag tag: Int) -> Int
+ func indexOfItem(representedObject obj: AnyObject?) -> Int
+ func indexOfItem(target target: AnyObject?, andAction actionSelector: Selector) -> Int

An example of a method family that does the same fundamental job but takes different parameters. To me, it makes a ton of sense that they have the same name and then overload it by parameters. You couldn’t do that in ObjC, but I suppose that’s how a Swift-first API would be designed. But if you do that, it’s good for readability to have the first parameter explicitly described.

Thanks for the feedback!

  - Doug

···

On Jan 27, 2016, at 2:09 AM, Radosław Pietruszewski <radexpl@gmail.com> wrote:

— Radek

On 27 Jan 2016, at 08:50, Douglas Gregor <dgregor@apple.com <mailto:dgregor@apple.com>> wrote:

On Jan 25, 2016, at 6:55 AM, Radosław Pietruszewski <radexpl@gmail.com <mailto:radexpl@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:

  Split method base name at the last "with" preposition. by DougGregor · Pull Request #5 · apple/swift-3-api-guidelines-review · GitHub

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

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/&gt;&quot;\)
    service.resource(absoluteURL: NSURL(string: "http://bar.com <http://bar.com/&gt;&quot;\))

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

Applying this heuristic changes the two “appendWithArcWithCenter” methods along, and one other place in the SDK:

- func isUnifiedWithContact(identifier contactIdentifier: String) -> Bool
+ func isUnified(contactWithIdentifier contactIdentifier: String) -> Bool

this one seems worse.

  - Doug

···

On Jan 27, 2016, at 11:22 AM, Ben Rimmington <me@benrimmington.com> wrote:

Doug,

On 27 Jan 2016, at 07:50, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:

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).

The related AppKit.NSBezierPath method contains two possible "With" separators:

- (void)appendBezierPathWithArcWithCenter:(NSPoint)center
                                  radius:(CGFloat)radius
                              startAngle:(CGFloat)startAngle
                                endAngle:(CGFloat)endAngle
                               clockwise:(BOOL)clockwise

Your patch replaces the second "With" separator, but the alternative might be better:

// YOUR PATCH:
func appendWithArc(center center: Point, radius: CGFloat, startAngle: CGFloat, endAngle: CGFloat, clockwise: Bool)

// THE ALTERNATIVE:
func append(arcWithCenter center: Point, radius: CGFloat, startAngle: CGFloat, endAngle: CGFloat, clockwise: Bool)

Doug,

I think this change looks great! I don’t have time to look through the full patch but did look through quite a bit. It adds clarity in the vast majority of cases I looked at.

It seems like with-as-separator is a good heuristic for determining when the first parameter is not essential to a good name for the fundamental operation. I agree with the comments earlier on that in these cases a label for the first parameter is the best approach.

I also really like that this groups methods with the same fundamental operation into overload families where they previously had independent names. This is a big win IMO.

There is a first-parameter-is-an-ID pattern I noticed after this change. I show a few examples here, but there are a lot more:

- func trackWithTrackID(trackID: CMPersistentTrackID) -> AVAssetTrack?
+ func track(trackID trackID: CMPersistentTrackID) -> AVAssetTrack?

- func trackWithTrackID(trackID: CMPersistentTrackID) -> AVFragmentedAssetTrack?
+ func track(trackID trackID: CMPersistentTrackID) -> AVFragmentedAssetTrack?

- func trackWithTrackID(trackID: CMPersistentTrackID) -> AVCompositionTrack?
+ func track(trackID trackID: CMPersistentTrackID) -> AVCompositionTrack?

- func discoverUserInfoWithUserRecordID(userRecordID: CKRecordID, completionHandler: (CKDiscoveredUserInfo?, Error?) -> Void)

+ func discoverUserInfo(userRecordID userRecordID: CKRecordID, completionHandler: (CKDiscoveredUserInfo?, Error?) -> Void)

The first argument label `trackID` seems like it repeats type information without adding clarity. I think it would be better to just use `id` here. It seems like a candidate for heuristics as well. For example, if the type name ends in ID and the label is a suffix of the type name we could just use `id`. This is a somewhat specific pattern, but IDs are common enough that it might make sense.

It affects 33 APIs; see attached patch.

Interestingly, in at least one case the `WithID` was the original name of the method so we did receive a simple `id` label:

- func parameterWithID(paramID: AudioUnitParameterID, scope: AudioUnitScope, element: AudioUnitElement) -> AUParameter?
+ func parameter(id paramID: AudioUnitParameterID, scope: AudioUnitScope, element: AudioUnitElement) -> AUParameter?

What’s happening here is that we see that “ID” in “WithID” is redundant, but we don’t want to provide a first argument label of “with”, so we grab whatever followed “with”.

In another case, the method has a naked `With` at the end. Somehow `id` was used in that scenario despite the parameter name being `objectID` and the type being `NSManagedObjectID`, which aligns with my suggested naming:

- func newValuesForObjectWith(objectID: NSManagedObjectID, withContext context: NSManagedObjectContext) throws -> NSIncrementalStoreNode
+ func newValuesForObject(id objectID: NSManagedObjectID, withContext context: NSManagedObjectContext) throws -> NSIncrementalStoreNode

This was originally newValuesForObjectWithID; again, we trimmed ID both before and after, and you’re seeing us keeping “id” because it’s better than keeping “with”.

A case related to that used `iDs` for array arguments:

- func managedObjectContextDidRegisterObjectsWithIDs(objectIDs: [NSManagedObjectID])
- func managedObjectContextDidUnregisterObjectsWithIDs(objectIDs: [NSManagedObjectID])
+ func managedObjectContextDidRegisterObjects(iDs objectIDs: [NSManagedObjectID])
+ func managedObjectContextDidUnregisterObjects(iDs objectIDs: [NSManagedObjectID])

I would prefer `ids` here. This seems like a pattern that would be a problem for any all-caps plural acronym or initialism so it might be good to add a heuristic for this as well.

Ah, it looks like I'm handling lowercasing of plural initialisms badly. That’s a problem independent of first argument labels; thanks!

Here’s another interesting change:

- func unionWith(s2: CIFilterShape) -> CIFilterShape
- func unionWith(r: CGRect) -> CIFilterShape
- func intersectWith(s2: CIFilterShape) -> CIFilterShape
- func intersectWith(r: CGRect) -> CIFilterShape
+ func union(with s2: CIFilterShape) -> CIFilterShape
+ func union(rect r: CGRect) -> CIFilterShape
+ func intersect(with s2: CIFilterShape) -> CIFilterShape
+ func intersect(rect r: CGRect) -> CIFilterShape

Why do the CGRect arguments receive a type-derived label but the CIFilterShape arguments just receive `with`? Shouldn’t these follow the same pattern?

The Objective-C methods are actually named unionWith: and unionWithRect:. That first name is not following Cocoa conventions.

  - Doug

universal-id.patch (12.5 KB)

···

On Jan 27, 2016, at 8:09 AM, Matthew Johnson <matthew@anandabits.com> wrote:

On Jan 27, 2016, at 1:50 AM, Douglas Gregor via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jan 25, 2016, at 6:55 AM, Radosław Pietruszewski <radexpl@gmail.com <mailto:radexpl@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:

  Split method base name at the last "with" preposition. by DougGregor · Pull Request #5 · apple/swift-3-api-guidelines-review · GitHub

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

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/&gt;&quot;\)
    service.resource(absoluteURL: NSURL(string: "http://bar.com <http://bar.com/&gt;&quot;\))

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@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

Patch where the words “ID”, “Identifier”, and “Name” in a name are considered to match the type “String”:

… and then extending the rule to zap first argument labels named “identifier”, “id”, or “name”:

(I’m not sure which one of these you meant, or something different):

  - Doug

id-identifier-name-match-string.patch (96.8 KB)

id-identifier-name-no-first-arg-label.patch (101 KB)

···

On Jan 27, 2016, at 10:03 AM, Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:

on Wed Jan 27 2016, Matthew Johnson <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Doug,

I think this change looks great! I don’t have time to look through
the full patch but did look through quite a bit. It adds clarity in
the vast majority of cases I looked at.

It seems like with-as-separator is a good heuristic for determining
when the first parameter is not essential to a good name for the
fundamental operation. I agree with the comments earlier on that in
these cases a label for the first parameter is the best approach.

I also really like that this groups methods with the same fundamental
operation into overload families where they previously had independent
names. This is a big win IMO.

There is a first-parameter-is-an-ID pattern I noticed after this
change. I show a few examples here, but there are a lot more:

- func trackWithTrackID(trackID: CMPersistentTrackID) -> AVAssetTrack?
+ func track(trackID trackID: CMPersistentTrackID) -> AVAssetTrack?

- func trackWithTrackID(trackID: CMPersistentTrackID) -> AVFragmentedAssetTrack?
+ func track(trackID trackID: CMPersistentTrackID) -> AVFragmentedAssetTrack?

- func trackWithTrackID(trackID: CMPersistentTrackID) -> AVCompositionTrack?
+ func track(trackID trackID: CMPersistentTrackID) -> AVCompositionTrack?

- func discoverUserInfoWithUserRecordID(userRecordID: CKRecordID,
completionHandler: (CKDiscoveredUserInfo?, Error?) -> Void)

+ func discoverUserInfo(userRecordID userRecordID: CKRecordID,
completionHandler: (CKDiscoveredUserInfo?, Error?) -> Void)

The first argument label `trackID` seems like it repeats type
information without adding clarity. I think it would be better to
just use `id` here. It seems like a candidate for heuristics as well.
For example, if the type name ends in ID and the label is a suffix of
the type name we could just use `id`. This is a somewhat specific
pattern, but IDs are common enough that it might make sense.

Actually I've been saying for a while that arguments called ID,
identifier, and name should not be labelled at all in many cases. Think
about it.

Matthew,

This is better IMO:

func appendArc(center center: Point, radius: CGFloat, startAngle: CGFloat, endAngle: CGFloat, clockwise: Bool)

However, I think this is an example of where explicit annotation is probably necessary to get the best result. A heuristic can’t always do the right thing and I think this example is past the point of diminishing returns for heuristics.

The "append" methods of the AppKit.NSBezierPath patch are:

   func append(path: NSBezierPath)
+ func append(rect rect: Rect)
+ func append(points points: PointArray, count: Int)
   func appendWithOvalIn(rect: Rect)
+ func appendWithArc(center center: Point, radius: CGFloat, startAngle: CGFloat, endAngle: CGFloat, clockwise: Bool)
+ func appendWithArc(center center: Point, radius: CGFloat, startAngle: CGFloat, endAngle: CGFloat)
   func appendWithArcFrom(point1: Point, to point2: Point, radius: CGFloat)
+ func append(glyph glyph: NSGlyph, in font: NSFont)
+ func append(glyphs glyphs: UnsafeMutablePointer<NSGlyph>, count: Int, in font: NSFont)
+ func append(packedGlyphs packedGlyphs: UnsafePointer<Int8>)
+ func append(roundedRect rect: Rect, xRadius: CGFloat, yRadius: CGFloat)

Would you need to annotate all the methods?

-- Ben

···

On 27 Jan 2016, at 19:26, Matthew Johnson <matthew@anandabits.com> wrote:

I’m not wild about using “by” in these names. I’d rather this were :

CIImage
  func compositedOverImage(dest: CIImage) -> CIImage
  func croppedTo(rect: CGRect) -> CIImage
  func clampedToExtent() -> CIImage

-jcr

···

On Jan 26, 2016, at 2:53 PM, Jacob Bandes-Storch via swift-evolution <swift-evolution@swift.org> wrote:

Here are some others from the swift-3 branch; I'm not sure where these currently fall under the guidelines:

CIImage
  func byCompositingOverImage(dest: CIImage) -> CIImage
  func byCroppingTo(rect: CGRect) -> CIImage
  func byClampingToExtent() -> CIImage

Doug,

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:

  Also split the first selector into base name/first argument label on … by DougGregor · Pull Request #8 · apple/swift-3-api-guidelines-review · GitHub

They look generally consistent with the “use a first argument label when the first parameter isn’t the direct object” guidelines being discussed.

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".

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

···

Sent from my iPhone
On Jan 30, 2016, at 8:03 AM, Radosław Pietruszewski <radexpl@gmail.com <mailto:radexpl@gmail.com>> wrote:

— Radek

On 27 Jan 2016, at 08:50, Douglas Gregor <dgregor@apple.com <mailto:dgregor@apple.com>> wrote:

On Jan 25, 2016, at 6:55 AM, Radosław Pietruszewski <radexpl@gmail.com <mailto:radexpl@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:

  Split method base name at the last "with" preposition. by DougGregor · Pull Request #5 · apple/swift-3-api-guidelines-review · GitHub

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

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/&gt;&quot;\)
    service.resource(absoluteURL: NSURL(string: "http://bar.com <http://bar.com/&gt;&quot;\))

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

One could imagine bridging NSURL to a value type URL (provided by Foundation, and initially implemented as a value-semantic wrapper around NSURL), the same way we bridge NSArray/NSDictionary/NSSet/NSString.

  - Doug

···

On Feb 1, 2016, at 1:38 PM, Drew Crawford <drew@sealedabstract.com> wrote:

On Feb 1, 2016, at 1:02 PM, Tony Parker <anthony.parker@apple.com <mailto:anthony.parker@apple.com>> wrote:

Types like NSURL are intended to be the canonical URL for everyone to use,

I think the "canonical URL for everyone to use" must be a value type / struct. To me this seems axiomatic.

Meanwhile (NS)URL cannot be a value type because of the Darwin compatibility mandate, and to change from value->reference semantics would be "off the charts" breakage severity.

This is a requirements conflict. corelibs-foundation can either be Darwin-compatible or it can be canonical, but we cannot do both. Right now we have chosen Darwin-compatible, so we must let the canonical requirement go. Unprefixed "URL" should be reserved for the value type that can actually work as a canonical API.