mvolkmann
(Mark Volkmann)
1
Frequently in my code I calls like this where there are in-scope variables named color, size, and temperature and I cannot change someMethod to take positional arguments:
someMethod(color: color, size: size, temperature: temperature)
I would love to eliminate the repetition, perhaps like this:
someMethod(color:, size:, temperature:)
If it is problematic for colors to appear here, maybe another character could be used like:
someMethod(color*, size*, temperature*)
1 Like
0x41c
(Corban Amouzou)
2
This seems like an interesting idea, but have you considered using underscores before the function identifiers to eliminate the need for any naming when passing in the arguments to the function? ex:
func someFunction(_ color: Color, _ size: CGSize, _ temperature: Double) {}
// Call the function without passing argument names
// Assume "color", "size", and "temperature" are already defined
func someFunction(color, size, temperature)
Those characters (the star and the colon) already have uses though, and shouldn't be re-used in such a context either.
beccadax
(Becca Royal-Gordon)
3
The thing is, in most argument lists it's poor Swift style to give a parameter a label that would also be a good variable name. For instance, your functionâs color, size, and temperature labels probably duplicate information available from the types of those parameters, which means theyâre redundant and you shouldnât use them. A more plausible example might look like:
someFunction(with: color, jelloCubeOf: size, at: temperature)
But with, jelloCubeOf, and at are not plausible names for a variable of those types, so you wouldnât be able to use that feature here. And that isnât a contrived example; the API Design Guidelines call for you to "prefer method and function names that make use sites form grammatical English phrasesâ, and arguments that are prefixed by nouns are not usually going to form grammatical English phrases.
There are a couple of cases where itâs idiomatic to use argument labels that might work as variable namesâoptional arguments, arguments to initializers and factory methods, etc.âbut these arenât super-common, and Iâm not convinced itâs worth adding a feature for them.
9 Likes
mvolkmann
(Mark Volkmann)
4
Yes, I know I can use understands in the function definitions to make the arguments positional, but typically I'm calling library functions that use named arguments.
Although I don't think the OP's idea is worth pursuing, because, although this sort of redundancy is common, it's not so common as to warrant a new feature, I also don't think the original intention of the following actually ended up looking as readable as intended:
Main reason: .init. But literals cause readability problems too.
someFunction(with: .init(gray: 0.5), jelloCubeOf: [1, 6], at: 33.2)
I never thought the bare prepositions were a good idea, and six years after the biggest change, I'm still not convinced. The separation of argument labels and parameter names is good, but primarily for removing prepositions from parameters, when the argument labels already include the direct objects.
4 Likes
mvolkmann
(Mark Volkmann)
6
Initializers are probably the most common place I run into this duplication. For example:
let person = Person(firstName: firstName, lastName: lastName, birthday: birthday)
2 Likes
That sounds like a use case for the ever-missing feature of applying functions, Optional.map-style, to everything. E.g.
let person = (firstName, lastName, birthday)âŠPerson.init
infix operator âŠ
///- Remark: Hold option, press ;
@discardableResult
public func ⊠<Input, Transformed>(
input: Input,
transform: (Input) -> Transformed
) -> Transformed {
transform(input)
}
2 Likes
tera
8
Possible solution would be to have a third case (optional parameter names) in addition to the two we already have: empty/external argument name (required parameter name) and "_" (no parameter name):
// not in Swift, straw-man syntax:
func someMethod(color _: Color, size _: Size, temperature _: Temperature) {}
// or this:
func someMethod(__ color: Color, __ size: Size, __ temperature: Temperature) {}
// or this:
func someMethod(~ color: Color, ~ size: Size, ~ temperature: Temperature) {}
// usage - can use either with or without parameter name:
someMethod(color: color, size: size, temperature: temperature) // ok
someMethod(color, size, temperature) // ok
someMethod(color: color, size, temperature) // still ok
mayoff
(Rob Mayoff)
9
This is a case where the duplication is helpful, because if firstName and lastName are the same type (probably String), it would be easy to mix them up without the labels, and with the labels, a glance is enough to notice if they're backward.
Your original example (where all the arguments are different types) is a case where I'd consider omitting the labels, because there's no possibility of mixing up the argument order. Or I might use a preposition to label the first argument and omit the labels of the rest.
3 Likes
What if the underscore were used in a reversed sense? For example, at the call site:
someMethod(color: _, size: _, temperature: _)
and the compiler will substitute the underscore with whatever variable has the same name as the argument name. I think the refactoring tool would need extra logic to recognise this if you tried to rename a variable using it.
mvolkmann
(Mark Volkmann)
11
Thereâs no issue with types because what is specified is a list of labels, not variables. It is saying âI have variables whose names match the labels, so use those as the values.â
masters3d
(Chéyo Jiménez)
12
This makes sense to me but it should be a feature of the editor not the compiler.
2 Likes
i understand the motivation of that guideline, but in practice trying to make API calls read like english sentences ends up making really brittle APIs that change frequently, are hard to refactor, and run counter to what would be the most logical organization from an implementation standpoint.
programming complex systems is hard enough without trying to be a poet at the same time, 75% of the time the best argument label is what the name of the variable would be in the calling scope.
of the common prepositions, as: and to: are probably the only useful argument labels. i tend to have a hard time choosing between of: and for:, and i canât remember many times where by: contributed any clarity at all.
6 Likes
tera
15
But it's obvious that data(for:delegate:) takes URL and data(from:delegate:) takes URLRequest... NOT!
(and it's the other way around)
Besides it's still not "English". "data(for: x, delegating: y)" would be...
There's no universal consistency. Sometimes "forKey" is used even when there's no other "forXxx", other times function is overloaded and it's "from:" argument accepts different types. Then there are some serious ĂŠsthetic offenders: CGAffineTransform(scaleX:y:)
I'd echo the previous two posts admitting that while this is not "swifty", oftentimes I'd personally prefer seeing some less grammatically correct form... e.g. something as simple as struct's default member-wise initialiser rules that results into Foo(bar:baz:) for a struct with bar and baz fields.
1 Like
zienag
(Alfred Zien)
16
I think itâs worth mentioning that Scala has very similar feature called implicit parameters. It might be interesting to see how itâs going there.
https://docs.scala-lang.org/tour/implicit-parameters.html
I vote no on this on this for the simple reason that swift should remain as exception free as possible. There is very little gain here.
2 Likes