Skip to content

Instantly share code, notes, and snippets.

@erica erica/namespacing.md
Last active Feb 6, 2017

Embed
What would you like to do?

Disambiguating SwiftPM Naming Conflicts

  • Proposal: TBD
  • Author(s): Erica Sadun
  • Status: TBD
  • Review manager: TBD

Introduction

This proposal creates a way to resolve SwiftPM module conflicts. It introduces namespacing to handle the rare case of name overlaps without requiring a central package registry.

This proposal was discussed on the Swift-Dev and Swift-Build-Dev lists in the "Right List?" thread.

Motivation

Swift offers a built in mechanism for distinguishing symbol conflicts. When working with NSView, using Swift.print outputs text to the console or stream. NSView's print creates a print job. Swift does not yet offer a solution for conflicting module names.

Like many other Swift developers, I have spent considerable time building utility packages. Mine use obvious names like SwiftString and SwiftCollections because simple clear naming is a hallmark of Swift design. At the same time, this style introduces the possibility of package name conflicts.

import SwiftString // mine
import SwiftString // someone else's. oops.

Two SwiftString packages cannot be used simultaneously without some form of namespace resolution. Moving back to Cocoa-style namespacing, for example ESSwiftString, feels ugly and antithetical to Swift. Swift should encourage recognizable, easy-to-understand module names. This proposal addresses this rare but possible conflict.

Under the current system, same-named modules:

import PackageDescription
let package = Package (
    name: "myutility",
        dependencies: [
	    .Package(url: "https://github.com/erica/SwiftString.git",
	             majorVersion: 1),
	    .Package(url: "https://github.com/nudas/SwiftString.git",
	             majorVersion: 1),
        ]

)

produce the following errors:

% swift build
Cloning Packages/SwiftString
Using version 1.0.5 of package SwiftString
Cloning Packages/SwiftString
/usr/bin/git clone --recursive --depth 10 https://github.com/nudas/SwiftString.git /home/erica/Work/test/Packages/SwiftString
fatal: destination path '/home/erica/Work/test/Packages/SwiftString' already exists and is not an empty directory.

swift-build: Failed to clone https://github.com/nudas/SwiftString.git to /home/erica/Work/test/Packages/SwiftString
Makefile:3: recipe for target 'all' failed
make: *** [all] Error 1

Detail Design

Under this proposal, the Swift Package manager uses reverse domain naming to differentiate otherwise identically-named items. The two conflicting packages in the following example:

import PackageDescription
let package = Package (
    name: "MyUtility",
    dependencies: [
	.Package(url: "https://github.com/erica/SwiftString.git",
                 majorVersion: 1),
	.Package(url: "https://github.com/bob/SwiftString.git",
                 majorVersion: 1),
    ]
)

unpack to Packages/com.github.erica.SwiftString and Packages/com.github.bob.SwiftString rather than Packages/SwiftString.

These can then be imported using the full RDN notation:

import com.github.erica.SwiftString
import com.github.bob.SwiftString

Reverse domain names

  • are rarely used outside the import statement and only to distinguish overlapping implementations
  • are relatively short
  • are already well established for Apple app distribution
  • are tied to the hosting repo

Future Directions

However concise, using reverse domain names bring verbiage to name conflicts. Upon adopting this proposal, I intend following through with language specific proposals that allow either import as or right-to-left namespacing.

A Swift import as statement would allow a developer to rename a module on import:

import com.github.erica.SwiftString as EricaString

A right-to-left alternative would require only as much of the reverse domain name prefix as needed to distinguish one implementation from another, for example:

erica.SwiftString.print(x)
bob.SwiftString.print(x)

although presumably the fully specified RDN prefix could be used as well:

com.github.erica.SwiftString.print(x)

Acknowledgements

Thanks to Joe Groff, Ankit Aggarwal, Max Howell, Daniel Dunbar, Kostiantyn Koval

@aciidb0mb3r

This comment has been minimized.

Copy link

aciidb0mb3r commented Feb 29, 2016

This sounds perfect!
Two things though:

  • Maybe localName could be importName or importAs
  • What happens when module names (maybe localName and name or both localName) clashes? Fail to build?
@erica

This comment has been minimized.

Copy link
Owner Author

erica commented Feb 29, 2016

Modified per suggestions. importAs scans better, so I chose that. Added note about clashes (should cause compilation error)

@habemus-papadum

This comment has been minimized.

Copy link

habemus-papadum commented Feb 6, 2017

Hi -- I came across this gist when encountering a module name conflict between two separate packages. Was this proposal ever implemented? Circa Feb 2017, is there a current solution to when ones needs to depend on both PackageA/moduleFoo and PackageB/moduleFoo. Sorry for posting here, was not sure where else I should try... Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.