- Proposal: SE-NNNN
- Author: Paul Cantrell
- Status: Awaiting review
- Review manager: TBD
This proposal provides a robust, generalized mechanism for altering the behavior and API design of third-party libraries.
Proposal SE-0117 proved controversial, with many on the swift-evolution list raising concerns about the proposal’s effect on clients of third-party libraries. Opponents of the proposal argued that discouraging subclassing would remove a valuable mechanism for externally altering undesired library behavior, and fixing the modeling mistakes that libraries authors inevitably make.
Proponents, however, countered that Swift already extensively discourages such retroactive modelling from the outside:
- Swift already makes types
internal
by default, and strictly enforces access control across modules. - Library authors may opt to make classes
final
, closing off retro-fixing by subclassing. - Structs and enums are not subclassable, so patching by subclassing is already unavailable for them.
- Members are not
dynamic
by default, so method swizzling is unavailable for most Swift methods.
Given these factors, even if subclassability remains available by default, it can only address a tiny fraction of the modeling problems which libraries will inevitably contain. Chris Lattner indicated on-list that SE-0117 should thus not be viewed in light of this external library fixing, and instead those concerned about the issue should propose a more robust solution to the problem if they wish it to be solved.
Furthermore, such modeling by subclassing does not notify library authors of the shortcomings of their code, causing possible duplication of effort between library clients and the loss of potential community benefit.
A custom subclass effectively functions as a memberwise delta from an existing class. The shortcomings listed above of fixing by subclassing boil down to three limitations of this delta: (1) it operates at the member level (2) of classes (3) whose access controls allow subclassing.
Instead, Swift should allow third parties to create a subclass-like delta that operates on any subset of a library’s AST. We will call this fully generalized delta against a module a knife, because it cuts down the barriers between library authors and library clients.
A knife consists of a zero or more deltas against the AST of a module, plus a canonical reference to the version of the module from which the delta was produced. For full generality, these deltas are encoded as substring replacements of a textual representation of the AST. (Ideally, this textual representation would be human-readable, but this is probably unrealistic given the complexity of the Swift language.)
Every knife keeps a canonical reference to the module it alters, and is pinned to a specific version of that module. When the underlying module changes, a knife may go out of sync. It is thus necessary to introduce a tool to manage the deltas comprising a knife. We will call this tool biter. Library authors use biter to store their modules, and knife authors use biter to keep their knives in sync with the modules they alter.
To facilitate collaboration between library authors and clients, a new web site, biternexus.com
, allows a knife author to send their knife to a library’s original author(s) as a tug proposal. If the library author chooses to accept a tug proposal, BiterFulcrum automatically generates a new version of the module including the knife’s modifications, and credits the knife author as a co-author of the parent module.
Of course, care must therefore be given to the command-line interface for biter
to ensure that it contains a confusing array of ill-named operations, ideally mixing high- and low-level concepts in ways that do not map to common use cases without the use of obscure command-line flags. This will prevent unwanted participation by casual coders, and will also help generate community engagement in neglected technical Q&A sites such as Queue Overrun. It will also ensure that students learning to use biter will regularly attend the office hours of their professors, who might otherwise be idle, bored, and thus inclined to invent dangerous new uses for category theory.
This combination of knife, biter, biternexus, and tug proposal addresses all the concerns of library clients, and far outstrips the capabilities of any mechanism available today.
Existing code may opt into this new mechanism without modification.
Although this is a non-breaking change, in order to advance the cause of liberty and the human spirit, it should be introduced immediately in a special release, Swift 2.2.1.
To allow knives to be written even without the consent of a module author, knives could consist of binary deltas against the compiled module instead of against the AST.