On Sun, Dec 27, 2015 at 10:40 PM, Douglas Gregor via swift-evolution < swift-evolution@swift.org> wrote:
On Dec 27, 2015, at 12:27 AM, Frederick Kellison-Linn via swift-evolution < > swift-evolution@swift.org> wrote:
Given that someView.insertSubview(_:at:) can be correctly parsed, I would
strongly lean towards the no-backtick alternative mentioned at the end. I
feel as though the backticks end up looking very cluttered (particularly
when you get into the double-nested backticks), and it seems cleaner to be
able to reference a method as it was declared rather than having to add
extra syntax.
In reference to the issues noted with this approach:
IMO, there is already enough syntactic difference between getters/setters
and normal methods to justify requiring a different syntax to reference
them. For instance, the # syntax could be used so that,
button.currentTitle.get would reference Optional<String>.get, and
button.currentTitle#get would reference the getter. Or,
button.`currentTitle.get` could reference the getter (i.e. backticks are
only required in cases that are ambiguous).
I also think it is reasonable to require that in the case of a method with
no arguments such as set.removeAllElements, the programmer be expected to
know the difference between the expression with and without the trailing
parenthesis. After all, that distinction already exists in the language,
and would not disappear with this proposed addition. If a parallel syntax
for referencing methods with no arguments is strongly desired, perhaps
something such as set.removeAllElements(:), set#removeAllElements(), or
similar could be used, though I think that the present system for
referencing these methods is sufficient.
Are there other obvious reasons why this alternative wouldn’t work? I
think it is the cleanest of the alternatives and avoids littering the code
with backticks.
Not having the back-ticks means that you will need to use contextual type
information to disambiguate the zero-parameter case from other cases. For
example:
class Foo {
func doSomething() { }
func doSomething(value: Int) { }
}
let fn = Foo.doSomething // ambiguous
let fn2 = Foo.doSomething(_:) // okay
let fn3: (Foo) -> () -> Void = Foo.doSomething // okay
let fn3: (Foo) -> (Int) -> Void = Foo.doSomething // okay
My general complaint with the “drop the backticks” approach is that it
doesn’t solve the whole problem. Sure, it solves 95% of the problem with a
little less syntax, but now you need to invent yet another mechanism to
handle the other cases (set, contextual type disambiguation, etc)… which
seems inconsistent to me.
- Doug
On Dec 26, 2015, at 11:22 PM, Douglas Gregor via swift-evolution < > swift-evolution@swift.org> wrote:
Hi all,
Here’s a proposal draft to allow one to name any function in Swift. In
effect, it’s continuing the discussion of retrieving getters and setters as
functions started by Michael Henson here:
[swift-evolution] Proposal: Expose getter/setters in the same way as regular methods
the proposal follows, and is available here as well:
https://github.com/DougGregor/swift-evolution/blob/generalized-naming/proposals/0000-generalized-naming.md
Comments appreciated!
Generalized Naming for Any Function
- Proposal: SE-NNNN
<https://github.com/apple/swift-evolution/blob/master/proposals/0000-generalized-naming.md>
- Author(s): Doug Gregor <https://github.com/DougGregor>
- Status: *Awaiting Review*
- Review manager: TBD
<https://github.com/DougGregor/swift-evolution/blob/generalized-naming/proposals/0000-generalized-naming.md#introduction>
Introduction
Swift includes support for first-class functions, such that any function
(or method) can be placed into a value of function type. However, it is not
possible to specifically name every function that is part of a Swift
program---one cannot provide the argument labels when naming a function,
nor are property and subscript getters and setters referenceable. This
proposal introduces a general syntax that allows one to name anything that
is a function within Swift in an extensible manner.
Swift-evolution thread: Michael Henson started a thread about the
getter/setter issue here
<https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151207/002168.html>,
continued here
<https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151214/002203.html>\.
See the Alternatives considered
<https://github.com/DougGregor/swift-evolution/blob/generalized-naming/proposals/0000-generalized-naming.md#alternatives-considered> section
for commentary on that discussion.
<https://github.com/DougGregor/swift-evolution/blob/generalized-naming/proposals/0000-generalized-naming.md#motivation>
Motivation
It's fairly common in Swift for multiple functions or methods to have the
same "base name", but be distinguished by parameter labels. For example,
UIView has three methods with the same base name insertSubview:
extension UIView {
func insertSubview(view: UIView, at index: Int)
func insertSubview(view: UIView, aboveSubview siblingSubview: UIView)
func insertSubview(view: UIView, belowSubview siblingSubview: UIView)
}
When calling these methods, the argument labels distinguish the different
methods, e.g.,
someView.insertSubview(view, at: 3)
someView.insertSubview(view, aboveSubview: otherView)
someView.insertSubview(view, belowSubview: otherView)
However, when referencing the function to create a function value, one
cannot provide the labels:
let fn = someView.insertSubview // ambiguous: could be any of the three methods
In some cases, it is possible to use type annotations to disambiguate:
let fn: (UIView, Int) = someView.insertSubview // ok: uses insertSubview(_:at:)let fn: (UIView, UIView) = someView.insertSubview // error: still ambiguous!
To resolve the latter case, one must fall back to creating a closure:
let fn: (UIView, UIView) = { view, otherView in
button.insertSubview(view, otherView)
}
which is painfully tedious. A similar workaround is required to produce a
function value for a getter of a property, e.g.,
extension UIButton {
var currentTitle: String? { ... }
}
var fn: () -> String? = { () in
return button.currentTitle
}
One additional bit of motivation: Swift should probably get some way to
ask for the Objective-C selector for a given method (rather than writing a
string literal). The argument to such an operation would likely be a
reference to a method, which would benefit from being able to name any
method, including getters and setters.
<https://github.com/DougGregor/swift-evolution/blob/generalized-naming/proposals/0000-generalized-naming.md#proposed-solution>Proposed
solution
Swift currently has a back-tick escaping syntax that lets one use keywords
for names, which would otherwise fail to parse. For example,
func `try`() -> Bool { ... }
declares a function named try, even though try is a keyword. I propose to
extend the back-tick syntax to allow compound Swift names (e.g.,
insertSubview(_:aboveSubview:)) and references to the accessors of
properties (e.g., the getter for currentTitle). Specifically,
-
Compound names can be written entirely within the back-ticks, e.g.,
let fn = someView.`insertSubview(_:at:)`let fn1 = someView.`insertSubview(_:aboveSubview:)`
The same syntax can also refer to initializers, e.g.,
let buttonFactory = UIButton.`init(type:)`
-
Getters and setters can be written using dotted syntax within the
back-ticks:
let specificTitle = button.`currentTitle.get` // has type () -> String?let otherTitle = UIButton.`currentTitle.get` // has type (UIButton) -> () -> String?let setTintColor = button.`tintColor.set` // has type (UIColor!) -> ()
The same syntax works with subscript getters and setters as well,
using the full name of the subscript:
extension Matrix {
subscript (row row: Int) -> [Double] {
get { ... }
set { ... }
}
}
let getRow = someMatrix.`subscript(row:).get` // has type (Int) -> () -> [Double]let setRow = someMatrix.`subscript(row:).set` // has type (Int) -> ([Double]) -> ()
If we introduce property behaviors into Swift, the back-tick syntax
could also be used to refer to behaviors, e.g., accessing the lazy behavior
of a property:
self.`myProperty.lazy`.clear()
-
Base names that are meaningful keywords (init and subscript) can be
escaped with a nested pair of back-ticks:
extension Font {
func `subscript`() -> Font {
// return the subscript version of the given font
}
}
let getSubscript = font.``subscript`()` // has type () -> Font
The "produce the Objective-C selector for the given method" operation will
be the subject of a separate proposal. However, here is one possibility
that illustrations how it uses the proposed syntax here:
let getter: Selector = objc_selector(NSDictionary.`subscript(_:).get`) // produces objectForKeyedSubscript:let setter: Selector = objc_selector(NSDictionary.`subscript(_:).set`) // produces setObject:forKeyedSubscript:
<https://github.com/DougGregor/swift-evolution/blob/generalized-naming/proposals/0000-generalized-naming.md#impact-on-existing-code>Impact
on existing code
This is a purely additive feature that has no impact on existing code. The
syntactic space it uses is already present, and it merely extends the use
of back-ticks from storing a single identifier to more complex names.
<https://github.com/DougGregor/swift-evolution/blob/generalized-naming/proposals/0000-generalized-naming.md#alternatives-considered>Alternatives
considered
-
Michael Henson proposed
<https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151207/002168.html> naming
getters and setters using # syntax followed by get or set, e.g.,
let specificTitle = button.currentTitle#get
The use of postfix # is a reasonable alternative here, and more
lightweight than two back-ticks for the simple getter/setter case. The
notion could be extended to allow argument labels for functions, discussed
here
<https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151214/002210.html>\.
The proposals in that discussion actually included type annotations as
well, but the syntax seems cleaner---and more directly focused on
*names*---without them, e.g.,:
let fn = someView.insertSubview#(_:at:)
which works. I didn't go with this syntax because (1) it breaks up
Swift method names such as insertSubview(_:at:)with an # in the
middle, and (2) while useful, this feature doesn't seem important enough to
justify overloading #further.
-
Joe Groff notes
<https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151214/003008.html>
that *lenses* are a better solution than manually retrieving
getter/setter functions when the intent is to actually operate on the
properties. That weakens the case this proposal makes for making
getters/setters available as functions. However, it doesn't address the
general naming issue or the desire to retrieve the Objective-C selector for
a getter/setter.
-
Can we drop the back-ticks? It's very tempting to want to drop the
back-ticks entirely, because something like
let fn = someView.insertSubview(_:at:)
can be correctly parsed as a reference to insertSubview(_:at:).
However, it breaks down at the margins, e.g., with getter/setter references
or no-argument functions:
extension Optional {
func get() -> T { return self! }
}
let fn1 = button.currentTitle.get // getter or Optional<String>.get?let fn2 = set.removeAllElements() // call or reference?
- Doug
_______________________________________________
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