When dealing with Python, you may see functions in which the last parameters can have any name (and type). This is achieved by using a dictionary and a custom splatting operator for dictionaries:
def foo(**kwargs):
print(kwargs.get('text', 'title'))
print(kwargs.get('color', 'black'))
print(kwargs.get('size', 14))
fun(text='My Text')
fun(text='My Colored Text', color='red')
fun(text='My Big Text', size=72)
This concept isn't available in Swift and functions like the one above are usually implemented in the following strictly typed way:
func foo(text: String = "title", color: Color = .black, size: Int = 14) {
print(text)
print(color)
print(size)
}
foo(text: "My Text")
foo(text: "My Colored Text", color = .red)
foo(text: "My Big Text", size: 72)
The benefit of **kwargs
is that you can type parameters in the order you prefer (omitting some parameters ― if you want to ― that will necessarily fallback to default values), but the downside is that you can't have an associated type to a specific parameter (e.g. having text: String
, size: Int
).
Swift, on the other end, forces you to type parameters in the order they appear in the function declaration (it suggests a fix-it to quickly resolve ordering issues, so that's not a problem) and autosuggests the whole function declaration:
With Xcode 12 you can now see all the parameters at once in the suggestion description.
However, it displays only one of the possibile initializer variants and even if you start typing a parameter name (such as exclusiveTouch
) it would fail autocompleting it:
How to solve custom ordering and autocompletion with Swift 5
If you really need custom ordering, autocompletion and strict typing you may decide to have an enum
associated to your function:
enum FooParameter {
case text(String)
case color(Color)
case size(Int)
}
func foo(_ kwargs: FooParameter...) {
var text: String = "title"
var color: Color = .black
var size: Int = 14
for arg in kwargs {
switch arg {
case .text(let x):
text = x
case .color(let x):
color = x
case .size(let x):
size = x
}
}
print(text)
print(color)
print(size)
}
foo(.color(.blue), .text("My Colored Small Text"), .size(8))
Xcode will only suggest the available parameter names (with repetition however) having their associated type in the description.
Proposed syntactic sugar
If the Swift community appreciates, I propose the following syntax to define typed **kwargs
:
func foo([text: String = "title", color: Color = .black, size: Int = 14]) {
print(text)
print(color)
print(size)
}
// all equivalent calls
foo(text: "My Colored Small Text", size: 8, color: .blue)
foo(text: "My Colored Small Text", color: .blue, size: 8)
foo(color: .blue, text: "My Colored Small Text", size: 8)
The following rules would apply:
-
kwargs
need to be declared in square brackets; -
kwargs
can only be declared in functions (not closures); -
kwargs
must be declared at the end of the function parameters list; -
kwargs
must provide default values for every parameter; -
kwargs
can only be declared once per function declaration.
Optionally:
- function overrides with the same "static" parameters (the ones outside the
[]
brackets) should throw an "Invalid redeclaration" compiler error; -
kwargs
may contain closures but they cannot be written as trailing closures on the call site.
Syntax reasoning
It's frequent in API documentation to denote optional arguments using square brackets, such as in Mozilla Developer Documentation:
arr.slice([start[, end]])
which means that the
end
parameter may be omitted, obtaining the following:arr.slice([start])
and the same applies for the
start
parameter, obtaining the following:arr.slice()
The meaning of the square brackets in the proposal is similar, they provide additional (unordered) parameters that can be omitted entirely.
Parameter definitions highly resemble the dictionary syntax.
How Xcode autosuggestions would work
Let us consider the following struct
declaration:
struct VGrid<Content>: View {
let content: Content
let columns: [GridItem]
let spacing: CGFloat
let alignment: HorizontalAlignment
let pinnedViews: PinnedScrollableViews?
init(content: Content, columns: [GridItem], [
spacing: CGFloat = 10,
alignment: HorizontalAlignment = .center,
pinnedViews: PinnedScrollableViews? = nil
]) { ... }
}
Typing "VGrid("
we should see:
VGrid(content:columns:)
VGrid(content:columns:alignment:...)
VGrid(content:columns:spacing:...)
VGrid(content:columns:pinnedViews:...)
Typing "VGrid(content: (...), columns: (...), s"
we should get autocompletion for "spacing" and the following suggestions:
VGrid(content:columns:spacing:)
VGrid(content:columns:spacing:alignment:...)
VGrid(content:columns:spacing:pinnedViews:...)
Typing "VGrid(content: (...), columns: (...), spacing: (...), a"
we should only see:
VGrid(content:columns:spacing:alignment:)
VGrid(content:columns:spacing:alignment:pinnedViews:)
Even though I'm suggesting how Xcode completion would work, I don't really know if its behavior is defined in the Language Server Protocol and so if the community can directly contribute to it.
The purpose of this thread is to discuss about the feature, to see how much or little consensus it can get from the community.
I don't personally think that it would be useful for the majority of the user base since function declarations with a long list of parameters are very unlikely to be found, but I may be wrong (especially for the custom ordering of parameters), so here's an informal poll in which you can vote for o against:
- Positive opinion
- Neutral opinion
- Negative opinion
0 voters