SE-0066: Standardize function type argument syntax to require parentheses
* Proposal: [SE-0066](0066-standardize-function-type-syntax.md) * Author: [Chris Lattner](https://github.com/lattner) * Review Manager: [Doug Gregor](https://github.com/DougGregor) * Status: **Implemented (Swift 3.0)** * Decision Notes: [Rationale](https://forums.swift.org/t/accepted-se-0066-standardize-function-type-argument-syntax-to-require-parentheses/2488) * Implementation: [apple/swift@3d2b5bc](https://github.com/apple/swift/commit/3d2b5bcc5350e1dea2ed8a0a95cd12ff5c760f24)
Introduction
Function types in Swift use parentheses around their parameter list (aligning with the function declaration syntax, as well as the syntax used to call a function). However, in the degenerate case of a single non-variadic, unlabeled argument with no attributes, Swift allows the parentheses to be omitted. For example, these types:
(Int) -> Float
(String) -> Int
(T) -> U
(Int) -> (Float) -> StringMay be written as:
Int -> Float
String -> Int
T -> U
Int -> Float -> StringWhile this saves some parentheses, it introduces some minor problems, is not consistent with other parts of the Swift grammar, reduces consistency within function types themselves, and offers no additional expressive capability (this is just syntactic sugar). This proposal suggests that we simply eliminate the special case and require parentheses on all argument lists for function types.
Swift-evolution thread: [\[pitch\] Eliminate the "T1 -> T2" syntax, require "(T1) -> T2"](https://forums.swift.org/t/pitch-eliminate-the-t1-t2-syntax-require-t1-t2/2211)
Motivation
Allowing this sugar introduces ambiguities in the language that require special rules to disambiguate. For example:
() -> Int // Takes zero arguments, or takes one zero-argument parameter?
(Int, Float) -> Int // Takes two arguments, or takes one two-argument tuple?This syntactic sugar reduces consistency with other parts of the language, since declarations always require parentheses, and calls requires parentheses as well. For example:
func f(a : Int) { ... } // ok
func f a : Int { ... } // my eyes!Finally, while it is straight-forward to remove this in Swift 3 (given the other migration that will be necessary to move Swift 2 code to Swift 3), removing this after Swift 3 will be much harder since we won't want to break code then. It is now or never.
History
The original rationale aligned with the fact that we wanted to treat all functions as taking a single parameter (which was often of tuple type) and producing a single value (which was sometimes a tuple, in the case of void and multiple return values). However, we’ve long since moved on from that early design point: there are a number of things that you can only do in a parameter list now (varargs, default args, internal vs API labels, etc), we removed implicit tuple splat, and the compiler has long ago stopped modeling function parameters this way.
Proposed solution
Parentheses will be required in function types. Examples:
Int -> Int // error
(Int) -> Int // function from Int to Int
((Int)) -> Int // also function from Int to Int
Int, Int -> Int // error
(Int, Int) -> Int // function from Int and Int to Int
((Int, Int)) -> Int // function from tuple (Int, Int) to Int
let f: () -> Int // function with no parameters
let g: (()) -> Int // function taking a single () parameter
let h: ((())) -> Int // function taking a single () parameter
f(); g(()); h(()) // correct
f(()); g(); h() // errorsFunction type grammar
Parentheses will become a part of function type grammar:
function-type → ( function-type-parameters<sub>opt</sub> ) throws-annotation<sub>opt</sub> -> type
function-type-parameters → function-type-parameter , function-type-parameters
function-type-parameters → function-type-parameter ...<sub>opt</sub>
function-type-parameter → attributes<sub>opt</sub> inout<sub>opt</sub> type
throws-annotation → throws | rethrows
Impact on existing code
The migrator will automatically add parentheses to existing code when moving from Swift 2 to Swift 3.