[swift-evolution] [Proposal draft] Generalized Naming for Any Function

Joe Groff jgroff at apple.com
Sun Dec 27 14:32:26 CST 2015


Some more things to consider:

- Our naming conventions encourage the first parameter to most methods to be unlabeled, so unlabeled parameters come up a lot. I don't think there's a grammatical requirement for an identifier before each colon; maybe we can leave out the underscore and use `foo(:bar:)` instead of `foo(_:bar:)` to refer to unlabeled arguments.

- How do labeled references interact with default and variadic arguments? If you have a func foo(x: Int = 0, y: String = 0), can you refer to foo(x:) and foo(y:) to apply some of the defaulted arguments, or only foo(x:y:)? Would foo(y:x:) also work?

-Joe

> On Dec 27, 2015, at 10:37 AM, Joe Groff via swift-evolution <swift-evolution at swift.org> wrote:
> 
>> 
>> On Dec 26, 2015, at 11:22 PM, Douglas Gregor via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at 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:
>> 
>> 	https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151207/002168.html <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151207/002168.html>
>> 
>> the proposal follows, and is available here as well:
>> 
>> 	https://github.com/DougGregor/swift-evolution/blob/generalized-naming/proposals/0000-generalized-naming.md <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:)`
> This part seems reasonable to me.
>> 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]) -> ()
> At least as far as pure Swift is concerned, for unapplied access, like `UIButton.currentTitle`, I think it would be more consistent with the way method references works for that to give you the getter (or lens) without decoration. instance.instanceMethod has type Args -> Ret, and Type.instanceMethod has type Self -> Args -> Ret; by analogy, since instance.instanceProperty has type Ret or inout Ret, it's reasonable to expect Type.instanceProperty to have type Self -> [inout] Ret. Forming a getter or setter partially applied to an instance feels unmotivated to me—{ button.currentTitle } or { button.currentTitle = $0 } already work, and are arguably clearer than this syntax.
> 
> I acknowledge that this leaves forming selectors from setters out to dry, but I feel like that's something that could be incorporated into a "lens" design along with typed selectors. As a rough sketch, we could say that the representation of @convention(selector) T -> inout U is a pair of getter/setter selectors, and provide API on Selector to grab the individual selectors from that, maybe Selector(getterFor: UIView.currentTitle)/(setterFor: UIView.currentTitle). I don't think get/set is a good interface for working with Swift properties, so I don't like the idea of building in language support to codify it beyond what's needed for ObjC interaction.
>> 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?
> From what I remember, the bigger concern with allowing foo(bar:bas:) without backticks is parser error recovery. The unambiguity with call syntax depends on having the `:)` token pair at the end. The edit distance between foo(bar:bas:) and a call foo(bar: bas) or work-in-progress call foo(bar: x, bas: ) is pretty slight, and would be tricky to give good diagnostics for. If we felt confident we could give good diagnostics, I'd support removing the backticks.
> 
> -Joe
> 
>  _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20151227/3489deb0/attachment.html>


More information about the swift-evolution mailing list