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

We would have to make that change to Darwin Foundation in the Swift 3 timeframe as well, since deviations from Darwin Foundation are currently considered bugs.

···

On Feb 1, 2016, at 3:40 PM, Douglas Gregor <dgregor@apple.com> wrote:

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.

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.

+1. I was pretty shocked by that sentence as well. The intention to canonize reference types just because they already exist could not be INVHO further from the right intention for Swift. I understand and accept the short-term rationale for leveraging them. I do not understand why we would settle for them as the long-term canonical solution.

···

On Feb 1, 2016, at 3:38 PM, Drew Crawford via swift-evolution <swift-evolution@swift.org> wrote:

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

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.

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

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.

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.

Sure, and that would be perfectly acceptable. If that (or something similar) is the long-term plan it seems like a particularly bad idea to drop `NS` from them right now.

That would mean we have a `URL` reference type in Swift 3 Foundation which becomes a value type in a future Swift X Foundation. This change would likely be very problematic.

···

On Feb 1, 2016, at 3:40 PM, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:

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

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

  - Doug

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

This is a major concern for me as well. I would much rather put up with NS prefixes for a while longer if it means we can improve on Foundation in the future. I think it would be very unfortunate if we forever locked ourselves into Swift anti-patterns like a reference-based URL type (among certainly many others) in what is intended to be like an extension to the Swift standard library.

Jarod

···

On Feb 1, 2016, at 13:38, Drew Crawford via swift-evolution <swift-evolution@swift.org> 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.

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.

But there are also many classes that make perfect sense in Swift as a class: URLSession, Formatter, UndoManager, UserDefaults, XMLParser, Stream, NotificationCenter, RunLoop, etc. What we could do is decide now which Foundation classes should stay classes and which should become structs. Remove the NS prefix from the former, keep it for the later.

Eventually, classes such as NSCountedSet and NSIndexSet will have a proper struct wrapper that will claim the non-prefixed name. But there is no urgency in making those wrappers ready for Swift 3 because the class remains available.

Anyway, that's what I'd propose. There aren't that many classes in Foundation; cases like this can be sorted out manually.

···

Le 5 févr. 2016 à 7:14, David Hart via swift-evolution <swift-evolution@swift.org> a écrit :

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

--
Michel Fortin
https://michelf.ca

The points made by Nate regarding his reasons for keeping the "NS" prefix
for Foundation types resonated with me, but for slightly different reasons.

In particular, where Nate states "Prefix-less Foundation types will blur
the different norms and expectations for Foundation types vs what we have
in the Swift standard library" contrasting Foundation and the Swift
standard library, this is very similar to my concern for being able to
differentiate between Foundation and app-specific custom types.

When working with an app's codebase, it is helpful to immediately be able
to tell where a particular type "comes from". Is this an instance of an
object defined in this app's codebase, or is it a Foundation object? There
are "different norms and expectations for Foundation types" in this
direction as well. Forcing readers of the code to click through to the
object type's definition (or perform a project-wide search) just to
discover the type is Foundation, is wasteful.

Swift's namespacing removes the technical requirement to prefix types as
became the norm in Objective-C. But there remain understandability
requirements that we should maintain.

Broadening the topic of prefixes beyond Foundation, it would be helpful to
keep the convention of prefixing types that are designed to be used as part
of a library, and leaving app-specific (or end-user-developer) types
unprefixed if desired.

Personally I am enjoying the safety of not having to prefix custom types in
my apps. But if I was writing a third-party library, I would want to use a
"signature prefix" on my types. Similarly, the NS prefix should remain on
Apple-supplied Foundation types.

This prefix-for-library-types convention would make it easier to quickly
grasp where a particular piece of functionality in a codebase is coming
from, so we can apply norms and expectations regarding the source.

Removing the NS prefix from Foundation types has large code
understandability/readability costs with little to no benefit for app
developers.

Regards,

···

----
Erwin Mazariegos
Austin, TX

On Fri, Feb 5, 2016 at 10:54 AM, Michel Fortin via swift-evolution < swift-evolution@swift.org> wrote:

Le 5 févr. 2016 à 7:14, David Hart via swift-evolution < > swift-evolution@swift.org> a écrit :

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

But there are also many classes that make perfect sense in Swift as a
class: URLSession, Formatter, UndoManager, UserDefaults, XMLParser, Stream,
NotificationCenter, RunLoop, etc. What we could do is decide now which
Foundation classes should stay classes and which should become structs.
Remove the NS prefix from the former, keep it for the later.

Eventually, classes such as NSCountedSet and NSIndexSet will have a proper
struct wrapper that will claim the non-prefixed name. But there is no
urgency in making those wrappers ready for Swift 3 because the class
remains available.

Anyway, that's what I'd propose. There aren't that many classes in
Foundation; cases like this can be sorted out manually.

--
Michel Fortin
https://michelf.ca

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

While I agree with this in theory, the problem with prefixing is what to choose; NS is nice and simple so it’s not much of a burden, but how many two letter prefixes can we expect before it gets confusing anyway, or you forget what the prefix is for?

Personally I’d prefer to make sure that Xcode exposes the namespace of a type as clearly as possible, e.g if you hover over the type it should expand to give a fully qualified name in addition to any documentation etc., leaving no ambiguity. In other words I wonder if name clashes is really a naming problem, or if it’s something that a good GUI should be able to handle for us. After all we can’t rely on every developer adding a prefix or suffix, and sometimes that commonly used name just happens to be the most appropriate.

I can’t count the number of times I’ve named something Node because I couldn’t think of anything more relevant, but likewise couldn’t find enough commonality to factor it out into a single type, resulting little nested Node types here and there. At least with the private keyword we can be clear that a developer need search no further, but I think it’s just a problem that can and will occur regardless of guidelines, so the tools need to be able to cope when it does.

···

On 5 Feb 2016, at 18:22, Erwin Mazariegos via swift-evolution <swift-evolution@swift.org> wrote:

Broadening the topic of prefixes beyond Foundation, it would be helpful to keep the convention of prefixing types that are designed to be used as part of a library, and leaving app-specific (or end-user-developer) types unprefixed if desired.

This is a very interesting topic.

I personally don’t use any prefix, but I’m still very careful to avoid names that are too general.

Looking at the main public types of my GitHub - groue/GRDB.swift: A toolkit for SQLite databases, with a focus on application development (I mean the types that the library user is likely to write in her own code, not the types of temporary values that are usually not written thanks to the Swift type inference), you should be able to guess what the library is about:

Concrete types:
- Configuration
- DatabaseQueue
- DatabaseMigrator
- Record
- Row
- SQLColumn

Errors:
- PersistenceError
- DatabaseError

Protocols:
- DatabaseValueConvertible
- Persistable
- RowConvertible
- TableMapping

The only exceptions are Configuration and Row. I did not feel like I should have named them more precisely because both types are very locally and contextually used. Conflict with user types is possible, but an explicit scoping with the module name is not too costly. That’s a feeling I can rationalize, but mainly it has not been (yet) contradicted by experience.

Record and Persistable are arguably database-ish enough for being kind of self-documenting without any further prefixing.

So, no, I don’t think prefixes are mandatory, or even should be generally recommended. Third-party libraries need their author to avoid likely naming conflicts. BTW, it’s an interesting sub-topic of the discussion about the very general Either and Result types: I can understand why some don’t feel well when they expose such a type in their library, and why they ask for a standard one.

Now, Foundation is a different story, because both Foundation and the Swift Standard Library want to provide basic needs. I would not be disappointed if Foundation would keep the NS prefix, so that the Swift Standard Library has room to expand. Just as Set was eventually introduced as an alternative to NSSet, regular expressions, paths manipulation, trees, etc. are all basic types that will be happy having a blessed Swift version in the future.

Gwendal

···

Le 5 févr. 2016 à 19:22, Erwin Mazariegos via swift-evolution <swift-evolution@swift.org> a écrit :

Personally I am enjoying the safety of not having to prefix custom types in my apps. But if I was writing a third-party library, I would want to use a "signature prefix" on my types. Similarly, the NS prefix should remain on Apple-supplied Foundation types.

Seems my mail got lost, so I'd like to add my voice to the concerns about removing the NS prefix expressed by Nate, David and others. As them I am strongly against dropping the NS prefix for types that are not Vey Swift-like meaning especially types which should be Swift value types (like an URL should be) but aren't.

I haven't followed the general discussion about this proposal closely except for this issue, so I won't say much about the rest of this proposal except that it did seem fine when glancing over the discussion.

-Thorsten

···

Am 05.02.2016 um 13:14 schrieb David Hart via swift-evolution <swift-evolution@swift.org>:

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> 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", 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
What 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

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

I know I’m late to the party and the review period has ended, but I’d like to add a suggestion towards a possible solution to a point that was discussed in this context.

[…]

Eventually, classes such as NSCountedSet and NSIndexSet will have a proper struct wrapper that will claim the non-prefixed name. But there is no urgency in making those wrappers ready for Swift 3 because the class remains available.

Anyway, that's what I'd propose. There aren't that many classes in Foundation; cases like this can be sorted out manually.

I wholeheartedly agree with this: I’d take a close look at every class in Foundation that should really be a value type in Swift (all collection types, NSURL, …) and keep the NS prefix for them. Then, at some later point (or with Swift 3 if there’s enough time), add struct wrappers without the NS prefix for these classes.

I want to outline a possible solution to this that allows all developers to annotate their classes and have them automatically imported as structs in Swift.
I think that could actually work with just a couple of annotations in the Objective-C headers and the correct interpretation of them in the Clang Importer. I’d propose something like this:

@interface NS_SWIFT_STRUCT(CountedSet) NSCountedSet<ObjectType> : NSMutableSet<ObjectType> {

@property BOOL foo; // Just for demonstration purposes.

- (NSUInteger)countForObject:(ObjectType)object;
- (void)addObject:(ObjectType)object NS_SWIFT_MUTATING;
- (void)removeObject:(ObjectType)object NS_SWIFT_MUTATING;

@end

In Swift, that would come through as the following (in addition to an NSCountedSet class):

struct CountedSet<ObjectType> {
    private let _internal: NSCountedSet

    var foo: Bool

    func countForObject(object: ObjectType) -> Int
    mutating func addObject(object: ObjectType)
    mutating func removeObject(object: ObjectType)

    …

}

That is: NS_SWIFT_STRUCT(_name) specifies that a struct with the given _name should be created that has a private let member of the annotated class. All methods of the class and all its superclasses are automatically imported as methods of the struct. Methods annotated as NS_SWIFT_MUTATING are imported as mutating methods in the Swift struct. For @property declarations, the importer could automatically infer that the setter is mutating and the getter nonmutating, although an NS_SWIFT_NONMUTATING might be useful to annotate that a property setter should be imported as nonmutating, if applicable.

After a quick glance over all of Foundation’s classes, I think the following could be eligible (probably not a complete list):
NSAttributedString
NSCharacterSet
NSCountedSet
NSData
NSDate
NSDecimalNumber
NSError
NSHashTable
NSIndexPath
NSIndexSet
NSNotification
NSNumber/NSValue
NSOrderedSet
NSURL
NSUUID

Whereas for some classes it would be better to use the mutable variants as the internal storage for the Swift struct:
NSMutableAttributedString (as AttributedString)
NSMutableCharacterSet (as CharacterSet)
NSMutableData (as Data)
NSMutableIndexSet (as IndexSet)
NSMutableOrderedSet (as OrderedSet)

The Clang Importer could also handle the case where passing a Swift URL to an Objective-C method that expects an NSURL could work automatically. Maybe an Objective-C methods like this:

- (void)methodImplementedInObjectiveC:(NSURL *)arg;

… could be imported into Swift like this:

func methodImplementedInObjectiveC(arg: NSURL)
func methodImplementedInObjectiveC(inout arg: URL) // Objective-C code operates on arg._internal

If the struct wraps a class that has a mutable counterpart (this would have to be annotated somehow), the import could do a better job for the nonmutating variant. This would require a variant of the NS_SWIFT_STRUCT(_name) macro that takes another parameter that points to the nonmutating variant. Then, the following methods:

- (void)nonmutatingMethodImplementedInObjectiveC:(NSData *)arg;
- (void)mutatingMethodImplementedInObjectiveC:(NSMutableData *)arg;

… could be imported into Swift like this:

func nonmutatingMethodImplementedInObjectiveC(arg: Data)
func mutatingMethodImplementedInObjectiveC(inout arg: Data) // Objective-C code operates on arg._internal

Annotations like this would be very useful because it would also allow developers to annotate their own classes like this. It’s not uncommon to have model objects that could sensibly be structs in Swift, but can’t because existing Objective-C code requires them to be classes.

When Objective-C classes become Swift structs, they obviously lose their inheritance chain and therefore polymorphic properties. I haven’t done a survey, but my gut feeling tells me that the classes for which all this is relevant – i.e. those that should have value semantics – aren’t usually subclassed and this point could therefore be moot. I may be wrong about that, though.

There are probably a lot of additional things to specify here, but maybe this could be a way forward. It would enable using things like NSCountedSet and NSURL in Swift just like it does now, with the possibility to add value types for them later without breaking existing code. In time, the classes could also be deprecated in favor of the struct, if that is desired.

Cheers,

Marco

···

On 2016-02-05, at 17:54, Michel Fortin via swift-evolution <swift-evolution@swift.org> wrote:

Good idea. Here are the diffs between 2 and 3:

Jacob

···

On Sat, Jan 23, 2016 at 1:28 PM, Dave Abrahams <dabrahams@apple.com> wrote:

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

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.

--
-Dave

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?

Not all of them, but it does appear to me like several of them would need annotation to get the best results, regardless of what you think the best results are. Which ones require annotation depends on what results you want of course.

···

On Jan 27, 2016, at 1:45 PM, Ben Rimmington <me@benrimmington.com> wrote:

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

-- Ben

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

Can I tell, at a call site, whether `respond(data: someData)` is responding
with someData, or responding to someData?

That is, can I tell whether someData is the response, or instead is
something from the request that the receiver will use to compute the
response?

If I see `respondWith(someData)` or `respondWith(data: someData)` or
`respondWithData(someData)`, it's clear to me that someData is the
response, while a hypothetical call `respondTo(someData)` would tell me
that someData is the input to the response-computing algorithm.

The second; it's AWESOME!

···

on Wed Jan 27 2016, Douglas Gregor <dgregor-AT-apple.com> wrote:

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

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

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

It affects 33 APIs; see attached patch.

The patch looks good!

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

Yep, I was just noting that we already ended up with `id` in some cases.

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.

Ok, that makes sense then. I didn’t think to look at the original Objective-C names. I wonder if it might be better to make these consistent though. Either discard the name if it matches the type:

func intersect(with r: CGRect) -> CIFilterShape

Or use the type information to create a name rather than use “with”:

func intersect(filterShape s2: CIFilterShape) -> CIFilterShape

What do you think?

···

On Jan 27, 2016, at 5:20 PM, Douglas Gregor <dgregor@apple.com> wrote:

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

  - Doug

<universal-id.patch>

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

Loss of 'with' sounds weird in certain cases:

- func account(identifier identifier: String!) -> ACAccount!
+ func account(identifier: String!) -> ACAccount!

···

Sent from my iPhone

On 28 Jan 2016, at 00:31, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:

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

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

<id-identifier-name-match-string.patch>

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

<id-identifier-name-no-first-arg-label.patch>

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

  - Doug

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

That’s a good point; the implementation is failing to remove the “by” in these cases. I’ll take a look.

  - Doug

···

On Jan 29, 2016, at 7:32 PM, John Randolph via swift-evolution <swift-evolution@swift.org> wrote:

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

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

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.

+1. I was pretty shocked by that sentence as well. The intention to canonize reference types just because they already exist could not be INVHO further from the right intention for Swift. I understand and accept the short-term rationale for leveraging them. I do not understand why we would settle for them as the long-term canonical solution.

Foundation is not a short term library. The reason we put it into Swift core libraries is because we intend for it to provide the most commonly needed functionality in one well-known, well-maintained place.

Dropping the NS is one part of how we intend to make Foundation a great part of Swift. We are working on other ways as well.

- Tony

···

On Feb 1, 2016, at 1:49 PM, Matthew Johnson <matthew@anandabits.com> wrote:

On Feb 1, 2016, at 3:38 PM, Drew Crawford via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

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

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.

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

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.

+1. I was pretty shocked by that sentence as well. The intention to canonize reference types just because they already exist could not be INVHO further from the right intention for Swift. I understand and accept the short-term rationale for leveraging them. I do not understand why we would settle for them as the long-term canonical solution.

Foundation is not a short term library. The reason we put it into Swift core libraries is because we intend for it to provide the most commonly needed functionality in one well-known, well-maintained place.

I don’t think anyone is thinking of Foundation as a short term library. I do think, though, that a lot of people will be disappointed if there is not a fairly short term plan to introduce types with value semantics into Foundation that would supersede some of the existing reference types. If that is going to happen, dropping NS would seem to complicate that significantly.

—CK

···

On Feb 1, 2016, at 2:08 PM, Tony Parker via swift-evolution <swift-evolution@swift.org> wrote:

On Feb 1, 2016, at 1:49 PM, Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>> wrote:

On Feb 1, 2016, at 3:38 PM, Drew Crawford via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

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

Dropping the NS is one part of how we intend to make Foundation a great part of Swift. We are working on other ways as well.

- Tony

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.

_______________________________________________
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 am not sure about these:

- func tracksWithMediaType(mediaType: String) -> [AVMovieTrack]
+ func tracks(mediaType mediaType: String) -> [AVMovieTrack]

- func tracksWithMediaCharacteristic(mediaCharacteristic: String) ->
+ func tracks(mediaCharacteristic mediaCharacteristic: String) -> [AVMovieTrack]

Using just tracks(…) is not as clear in my opinion. I could get used to it, but I think the before version is more clear than the after version.

An alternative could be to have one method name for all different ways to get tracks, i.e. tracksWith(…) or tracksMatching(…) with different parameters each. Or just leave them the way they are, i.e. tracksWithMediaType(…) and tracksWithMediaCharacteristic(…).

Getting rid of With in other methods does seem to work though. For example, encode(coder:)

···

On Feb 1, 2016, at 3:04 PM, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:

Sent from my iPhone

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

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

— 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

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