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


(Colin Cornaby) #1

I think there are some rough edges here that could be refined, but as a long time Obj-C guy, I'm fine leaving prefixes behind. They've frequently been misused. Third parties are supposed to use three letter prefixes, but frequently only use two, increasing the chances of a collision. Name prefixes to me have seemed like a ticking time bomb. I have nightmares about us or a dependency colliding with Apple's prefixes in an OS update, especially now that I've seen a few three letter prefixes coming from Apple.

I also don't think prefixes fit well with Swift Foundation, and Swift Foundation shouldn't diverge that significantly from Apple Foundation.

I think there are several ways of dealing with the issues raised here:
- Promote defining variables by their fully quantified type names. I wasn't even sure that I could do Foundation.NSString until I tried it just now.
- Keep the hierarchies small. What always drives me crazy in languages like Java (and that C# example) is the huge hierarchies. We should probably strive to keep things simple. Foundation.String, not System.Utils.Foundation.Text.String.

Something like CoreImage.Image and AppKit.Image is a little more bulky, but I think it actually reads better.

I don't have strong opinions on selectively importing portions of modules. But if that was to be promoted as a development methodology, I'd like Xcode to automatically manage my imports, much like Eclipse and Android Studio do. When I umbrella import a module, I typically do so out of laziness, so having the IDE take care of my imports for me is a nice compromise.

···

On Feb 01, 2016, at 10:21 AM, 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:

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