mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
507 lines
25 KiB
ReStructuredText
507 lines
25 KiB
ReStructuredText
:orphan:
|
|
|
|
Unified Function Syntax via Selector Splitting
|
|
==============================================
|
|
|
|
.. warning:: This document was used in planning Swift 1.0; it has not been kept
|
|
up to date and does not describe the current or planned behavior of Swift. In
|
|
particular, we experimented with preposition-based splitting and decided
|
|
against it.
|
|
|
|
.. contents::
|
|
|
|
Cocoa Selectors
|
|
---------------
|
|
A Cocoa selector is intended to convey what a method does or produces
|
|
as well as what its various arguments are. For example,
|
|
``NSTableView`` has the following method::
|
|
|
|
- (void)moveRowAtIndex:(NSInteger)oldIndex toIndex:(NSInteger)newIndex;
|
|
|
|
Note that there are three pieces of information in the selector
|
|
``moveRowAtIndex:toIndex:``:
|
|
|
|
1. What the method is doing ("moving a row").
|
|
2. What the first argument is ("the index of the row we're moving").
|
|
3. What the second argument is ("the index we're moving to").
|
|
|
|
However, there are only two selector pieces: "moveRowAtIndex" and
|
|
"toIndex". The first selector piece is conveying both #1 and #2, and
|
|
it reads well in English because the preposition "at" separates the
|
|
action (``moveRow``) from the first argument (``AtIndex``), while the
|
|
second selector piece conveys #3. Cocoa conventions in this area are
|
|
fairly strong, where the first selector piece describes what the
|
|
operation is doing or produces, and well as what the first argument
|
|
is, and subsequent selector pieces describe the remaining arguments.
|
|
|
|
Splitting Selectors at Prepositions
|
|
-----------------------------------
|
|
When importing an Objective-C selector, split the first selector piece
|
|
into a base method name and a first argument name. The actual split
|
|
will occur just before the last preposition in the selector piece,
|
|
using camelCase word boundaries to identify words. The resulting
|
|
method name is::
|
|
|
|
moveRow(atIndex:toIndex:)
|
|
|
|
where ``moveRow`` is the base name, ``atIndex`` is the name of the
|
|
first argument (note that the 'a' has been automatically lowercased),
|
|
and ``toIndex`` is the name of the second argument.
|
|
|
|
In the (fairly rare) case where there are two prepositions in the
|
|
initial selector, splitting at the last preposition improves the
|
|
likelihood of a better split, because the last prepositional phrase is
|
|
more likely to pertain to the first argument. For example,
|
|
``appendBezierPathWithArcFromPoint:toPoint:radius:`` becomes::
|
|
|
|
appendBezierPathWithArc(fromPoint:toPoint:radius:)
|
|
|
|
If there are no prepositions within the first selector piece, the
|
|
entire first selector piece becomes the base name, and the first
|
|
argument is unnamed. For example ``UIView``'s
|
|
``insertSubview:atIndex:`` becomes::
|
|
|
|
insertSubview(_:atIndex:)
|
|
|
|
where '_' is a placeholder for an argument with no name.
|
|
|
|
Calling Syntax
|
|
--------------
|
|
By splitting selectors into a base name and argument names, Swift's
|
|
keyword-argument calling syntax works naturally::
|
|
|
|
tableView.moveRow(atIndex: i, toIndex: j)
|
|
view.insertSubview(someView, atIndex: i)
|
|
|
|
The syntax generalizes naturally to global and local functions that
|
|
have no object argument, i.e.,::
|
|
|
|
NSMakeRange(location: loc, length: len)
|
|
|
|
assuming that we had argument names for C functions or a Swift overlay
|
|
that provided them. It also nicely handles cases where argument names
|
|
aren't available, e.g.,::
|
|
|
|
NSMakeRange(loc, len)
|
|
|
|
as well as variadic methods::
|
|
|
|
NSString(stringwithFormat: "%@ : %@", key, value)
|
|
|
|
Declaration Syntax
|
|
------------------
|
|
The existing "selector-style" declaration syntax can be extended to
|
|
better support declaring functions with separate base names and first
|
|
argument names, i.e.::
|
|
|
|
func moveRow atIndex(Int) toIndex(Int)
|
|
|
|
However, this declaration looks very little like the call site, which
|
|
uses a parenthesized argument list, commas, and colons. Let's
|
|
eliminate the "selector-style" declaration syntax entirely. We can use
|
|
the existing ("tuple-style") declaration syntax to mirror the call
|
|
syntax directly::
|
|
|
|
func moveRow(_ atIndex: Int, toIndex: Int)
|
|
|
|
Now, sometimes the argument name that works well at the call site
|
|
doesn't work well for the body of the function. For example, splitting
|
|
the selector for ``UIView``'s ``contentHuggingPriorityForAxis:``
|
|
results in::
|
|
|
|
func contentHuggingPriority(_ forAxis: UILayoutConstraintAxis) -> UILayoutPriority
|
|
|
|
The name ``forAxis`` works well at the call site, but not within the
|
|
function body. So, we allow one to specify the name of the parameter
|
|
for the body of the function::
|
|
|
|
func contentHuggingPriority(forAxis axis: UILayoutConstraintAxis) -> UILayoutPriority {
|
|
// use 'axis' in the body
|
|
}
|
|
|
|
One can use '_' in either the argument or parameter name position to
|
|
specify that there is no name. For example::
|
|
|
|
func f(_ a: Int) // no argument name; parameter name is 'a'
|
|
func g(b _: Int) // argument name is 'b'; no parameter name
|
|
|
|
The first function doesn't support keyword arguments; it is what an
|
|
imported C or C++ function would use. The second function supports a
|
|
keyword argument (``b``), but the parameter is not named (and
|
|
therefore cannot be used) within the body. The second form is fairly
|
|
uncommon, and will presumably only to be used for backward
|
|
compatibility.
|
|
|
|
Method Names
|
|
------------
|
|
The name of a method in this scheme is determined by the base name and
|
|
the names of each of the arguments, and is written as::
|
|
|
|
basename(param1:param2:param3:)
|
|
|
|
to mirror the form of declarations and calls, with types, arguments,
|
|
and commas omitted. In code, one can refer to the name of a function
|
|
just by its basename, if the context provides enough information to
|
|
uniquely determine the method. For example, when uncurrying a method
|
|
reference to a variable of specified type::
|
|
|
|
let f: (UILayoutConstraintAxis) -> UILayoutPriority = view.contentHuggingPriority
|
|
|
|
To refer to the complete method name, place the method name in
|
|
backticks, as in this reference to an optional method in a delegate::
|
|
|
|
if let method = delegate.`tableView(_:viewForTableColumn:row:)` {
|
|
// ...
|
|
}
|
|
|
|
Initializers
|
|
------------
|
|
Objective-C ``init`` methods correspond to initializers in
|
|
Swift. Swift splits the selector name after the ``init``. For example,
|
|
``NSView``'s ``initWithFrame:`` method becomes the initializer::
|
|
|
|
init(withFrame: NSRect)
|
|
|
|
There is a degenerate case here where the ``init`` method has
|
|
additional words following ``init``, but there is no argument with
|
|
which to associate the information, such as with
|
|
``initForIncrementalLoad``. This is currently handled by adding an
|
|
empty tuple parameter to store the name, i.e.::
|
|
|
|
init(forIncrementalLoad:())
|
|
|
|
which requires the somewhat unfortunate initialization syntax::
|
|
|
|
NSBitmapImageRep(forIncrementalLoad:())
|
|
|
|
Fortunately, this is a relatively isolated problem: Cocoa and Cocoa
|
|
Touch contain only four selectors of this form::
|
|
|
|
initForIncrementalLoad
|
|
initListDescriptor
|
|
initRecordDescriptor
|
|
initToMemory
|
|
|
|
With a number that small, it's easy enough to provide overlays.
|
|
|
|
Handling Poor Mappings
|
|
----------------------
|
|
The split-at-last-preposition heuristic works well for a significant
|
|
number of selectors, but it is not perfect. Therefore, we will
|
|
introduce an attribute into Objective-C that allows one to specify the
|
|
Swift method name for that Objective-C API. For example, by default,
|
|
the ``NSURL`` method ``+bookmarkDataWithContentsOfURL:error:`` will
|
|
come into Swift as::
|
|
|
|
class func bookmarkDataWithContents(ofURL bookmarkFileURL: NSURL, error: inout NSError) -> NSData
|
|
|
|
However, one can provide a different mapping with the ``method_name``
|
|
attribute::
|
|
|
|
+ (NSData *)bookmarkDataWithContentsOfURL:(NSURL *)bookmarkFileURL error:(NSError **)error __attribute__((method_name(bookmarkData(withContentsOfURL:error:))))
|
|
|
|
This attribute specifies the Swift method name corresponding to that
|
|
selector. Presumably, the ``method_name`` attribute will be wrapped in
|
|
a macro supplied by Foundation, i.e.,::
|
|
|
|
#define NS_METHOD_NAME(Name) __attribute__((method_name(Name)))
|
|
|
|
For 1.0, it is not feasible to mark up the Objective-C headers in the
|
|
various SDKs. Therefore, the compiler will contain a list of mapping
|
|
from Objective-C selectors to Swift method names. Post-1.0, we can
|
|
migrate these mappings to the headers.
|
|
|
|
A mapping in the other direction is also important, allowing one to
|
|
associate a specific Objective-C selector with a method. For example,
|
|
a Boolean property::
|
|
|
|
var enabled: Bool {
|
|
@objc(isEnabled) get {
|
|
// ...
|
|
}
|
|
|
|
set {
|
|
// ...
|
|
}
|
|
}
|
|
|
|
Optionality and Ordering of Keyword Arguments
|
|
---------------------------------------------
|
|
A number of programming languages have keyword arguments in one form
|
|
or another, including Ada, C#, Fortran 95, Lua, OCaml,
|
|
Perl 6, Python, and Ruby. Objective-C and Smalltalk's use of selectors
|
|
is roughly equivalent, in the sense that the arguments get names.
|
|
The languages with keyword arguments (but not Objective-C and
|
|
Smalltalk) all allow re-ordering of
|
|
arguments at the call site, and many allow one to
|
|
provide arguments positionally without their associated name at the
|
|
call site. However, Cocoa APIs were designed based on the
|
|
understanding that they would not be re-ordered, and the sentence
|
|
structure of some selectors depends on that. To that end, a new
|
|
attribute ``call_arguments(strict)`` can be placed on any function and
|
|
indicates that keyword arguments are required and cannot be reordered
|
|
in calls to that function, i.e.::
|
|
|
|
@call_arguments(strict)
|
|
func moveRow(_ atIndex:Int, toIndex:Int)
|
|
|
|
Swift's Objective-C importer will automatically add this to all
|
|
imported Objective-C methods, so that Cocoa APIs will retain their
|
|
sentence structure.
|
|
|
|
Removing ``with`` and ``for`` from Argument Names
|
|
-------------------------------------------------
|
|
The prepositions ``with`` and ``for`` are commonly used in the first
|
|
selector piece to separate the action or result of a method from the
|
|
first argument, but don't themselves convey much information at either
|
|
the call or declaration site. For example, ``NSColor``'s
|
|
``colorWithRed:green:blue:alpha:`` is called as::
|
|
|
|
NSColor.color(withRed: 0.5, green: 0.5, blue: 0.5, alpha: 1.0)
|
|
|
|
The ``with`` in this case feels spurious and makes ``withRed`` feel
|
|
out of sync with ``green``, ``blue``, and ``alpha``. Therefore, we
|
|
will remove the ``with`` (or ``for``) from any argument name, so that
|
|
this call becomes::
|
|
|
|
NSColor.color(red: 0.5, green: 0.5, blue: 0.5, alpha: 1.0)
|
|
|
|
In addition to improving the call site, this eliminates the need to
|
|
rename parameters as often at the declaration site, i.e., this::
|
|
|
|
class func color(withRed red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) -> NSColor
|
|
|
|
becomes::
|
|
|
|
class func color(_ red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) -> NSColor
|
|
|
|
Note that we only perform this removal for ``with`` and ``for``; other
|
|
prepositions tend to have important meaning associated with them, and
|
|
are therefore not removed. For example, consider calls to the
|
|
``NSImage`` method ``-drawInRect:fromRect:operation:fraction:`` with
|
|
the leading prepositions retained and removed, respectively::
|
|
|
|
image.draw(inRect: x, fromRect: x, operation: op, fraction: 0.5)
|
|
image.draw(rect: x, rect: y, operation: op, fraction: 0.5)
|
|
|
|
Here, dropping the leading prepositions is actively harmful, because
|
|
we've lost the directionality provided by ``in`` and ``from`` in the
|
|
first two arguments. ``with`` and ``for`` do not have this problem.
|
|
|
|
The second concern with dropping ``with`` and ``for`` is that we need
|
|
to either specify or infer the prepositions when declaring a
|
|
method. For example, consider the following initializer::
|
|
|
|
init(frame: CGRect)
|
|
|
|
How would the compiler know to insert the preposition "with" into the
|
|
name when computing the selector, so that this maps to
|
|
``initWithFrame:``? In many cases, where we're overriding a method or
|
|
initializer from a superclass or we are implementing a method to conform
|
|
to a protocol, the selector can be deduced from method/initializer in
|
|
the superclass or protocol. In those cases where new API is being
|
|
defined in Swift where the selector requires a preposition, one would
|
|
use the ``objc`` attribute with a selector::
|
|
|
|
@objc(initWithFrame:)
|
|
init(frame: CGRect)
|
|
|
|
Imported Objective-C methods would have the appropriate ``objc``
|
|
attribute attached to them automatically.
|
|
|
|
Which Prepositions?
|
|
-------------------
|
|
|
|
English has a large number of prepositions, and many of those words
|
|
also have other rules as adjectives, adverbs, and so on. The following
|
|
list, taken from `The English Club`_, with poetic, archaic, and non-US
|
|
forms removed, provided the starting point for the list of
|
|
prepositions used in splitting. The **bolded** prepositions are used
|
|
to split; notes indicate whether Cocoa uses this preposition as a
|
|
preposition in any of its selectors, as well as any special
|
|
circumstances that affect inclusion or exclusion from the list.
|
|
|
|
+----------------+---------+---------+----------------------------+
|
|
|Preposition |In Cocoa?|Dropped? | Notes |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Aboard | No | | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| About | No* | | Used as an adjective |
|
|
+----------------+---------+---------+----------------------------+
|
|
| **Above** | Yes | No | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Across | No | | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| **After** | Yes | No | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Against | Yes* | | Misleading when split |
|
|
+----------------+---------+---------+----------------------------+
|
|
| **Along** | Yes | No | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| **Alongside** | Yes | No | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Amid | No | | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Among | No | | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Anti | No* | | Used as an adjective |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Around | No | | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| **As** | Yes | No | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Astride | No | | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| **At** | Yes | No | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Bar | No* | | Used as a noun |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Barring | No | | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| **Before** | Yes | No | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Behind | No | | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| **Below** | Yes | No | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Beneath | No | | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Beside | No | | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Besides | No | | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Between | Yes | | Not amenable to parameters |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Beyond | No | | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| But | No | | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| **By** | Yes | No | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Circa | No | | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Concerning | No | | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Considering | No | | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Counting | No* | | Used as an adjective |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Cum | No | | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Despite | No | | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Down | No* | | Used as a noun |
|
|
+----------------+---------+---------+----------------------------+
|
|
| During | Yes* | | Misleading when split |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Except | No | | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Excepting | No | | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Excluding | No | | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| **Following** | Yes | No | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| **For** | Yes | **Yes** | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| **From** | Yes | No | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| **Given** | Yes* | No | Never splits a selector |
|
|
+----------------+---------+---------+----------------------------+
|
|
| **In** | Yes | No | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| **Including** | Yes* | No | Never splits a selector |
|
|
+----------------+---------+---------+----------------------------+
|
|
| **Inside** | Yes | No | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| **Into** | Yes | No | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Less | No* | | Always "less than" |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Like | Yes* | | Misleading when split |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Minus | No | | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Near | No | | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Notwithstanding| No | | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| **Of** | Yes | No | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Off | No* | | Used as a noun |
|
|
+----------------+---------+---------+----------------------------+
|
|
| **On** | Yes | No | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Onto | No | | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Opposite | No | | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Out | No* | | Used as an adverb |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Outside | Yes* | | Misleading when split |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Over | No* | | Used as an adverb |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Past | No | | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Pending | No* | | Used as an adjective |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Per | Yes* | | Misleading to split |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Plus | No | | Used as an adjective |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Pro | No | | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Regarding | No | | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Respecting | No | | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Round | No | | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Save | No* | | Used as adjective, verb |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Saving | No* | | Used as adjective |
|
|
+----------------+---------+---------+----------------------------+
|
|
| **Since** | Yes | No | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Than | No* | | Always "greater than" |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Through | Yes* | | Misleading when split |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Throughout | No | | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| **To** | Yes | No | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Toward | No | | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Towards | No | | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Under | No | | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Underneath | No | | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Unlike | No | | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| **Until** | Yes | No | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Unto | No | | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Up | No* | | Used as adjective |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Upon | Yes* | | Misleading when split |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Versus | No | | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| **Via** | Yes | No | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| **With** | Yes | **Yes** | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| **Within** | Yes | No | |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Without | Yes* | | Misleading when split |
|
|
+----------------+---------+---------+----------------------------+
|
|
| Worth | No | | |
|
|
+----------------+---------+---------+----------------------------+
|
|
|
|
.. _the english club: https://www.englishclub.com/grammar/prepositions-list.htm
|