Named parameter HORROR

Two days into my Swift (2) experience...


func X( a:Int,  b:Int) {
    print(a,b)
}

X(  1,   2) // error: missing argument label 'b:' in call
X(a:1,   2) // error: incorrect argument labels in call (have 'a:_:', expected '_:b:')
X(  1, b:2) // ok
X(a:1, b:2) // error: extraneous argument label 'a:' in call


Horror!


😢😮😠😕😟➖⚠✖


And I *never* use Emoji.


Contrast this with:


print( [1,2,3].contains(2) )


Perfect, I didn't know any of that syntax. But that's what I would expect. That's 'Pythonic'. Writing out my idea in straight pseudocode, and it works! Isn't that the goal of all of this? Getting the most natural effortless straightforward syntax?


That "named parameter mess" reminds me of C++, where everything breaks down into a zillion edge cases.


(On a side note, I wasted a lot of effort trying to get named parameters past the C++ committee. So there is some irony in the fact that although Swift has done it, it has in-so-doing created a C++-style awfulness).


I am making a bad smell here for a reason. Because I've watched the videos, I've read the book introduction. From my experience with C/ObjC/C++/C#/Python/JavaScript I love what's going on -- the idea of starting from scratch making a truly modern language with Pythonesque grace and C-like performance. The moment the opensource move hit of the news I jumped on it.


If I didn't think it was a great language I wouldn't complain. I would just silently walk away.


But we can't have fugliness like this.


All 4 of those cases should work. So that whether or not you supply a name for an argument is up to you.


drawCircle(position: myPosition,  radius: myRadius) // BAD -- duplication is ugly
drawCircle(myPosition, myRadius, borderWidth:1)     // GOOD


That's zen.


I'm sure there is some longwinded explanation for why the above catastrophe has happened. I'm not interested. I just want to see it gone. I want to teach this language to children.


I can see clearly the vision of Swift, and I am concerned the reality keeps to it.


π

I agree, it's a bit stange ; the point is that the first parameter name is considered as part of the function, when following must be declared;

Look at Swift 2, ibook page 277, where the rules for external parameters names are explained.

You don't actually say what you think is wrong with the parameter keyword rules, but I think you're trying to say that parameter keywords should be optional in the caller.


If that were the rule, then Swift code wouldn't be able to distinguish between some pairs of Obj-C method signatures, which differ only in the keyword. That would prevent Swift from being interoperable with Obj-C, and that would pretty much prevent Swift from being widely adopted in the general Apple developer community, at least any time soon.

Note that I'm talking about optionality in the caller here. Keywords are already optional (in a different sense) in the method declaration:

funcY ( _ a:Int, _  b:Int)
Y (1, 2) // ok

but of course the caller must stil adopt the style set by the declaration:

Y (a: 1, b: 2) // not ok

I also think (though of course I'm not sure) that the Swift language designers thought that non-optionality also produced clearer code generally, at the expense of some duplication sometimes, such as in your 'drawCircle' example. You may disagree with their choice, but if there is a choice to be made, they have to choose something, and someone or other is going to disagree.

Parameter names are as important to making an API understandable as the method names themselves. In some calls, parameter names would be completely superfluous:

min(a, b) vs. min(first: a, second: b)


While in others, they're essential to understanding what the code means:

stride(from: 1, through: 10, step: 2) vs. stride(1, 10, 2)


Moreover, sometimes labels are used to distinguish between otherwise identical calls. I currently have a playground open where I have the following methods implemented:

stack.insertLayer(newLayer, afterLayer: oldLayer)
stack.insertLayer(newLayer, beforeLayer: oldLayer)

If parameter labels were optional, it'd be impossible to distinguish between calls to these two methods. And if you renamed the methods to "insertLayerBeforeLayer" and "insertLayerAfterLayer", the code would read differently (and less clearly, I believe).


So, in the opinion of Swift's designers, parameter names are something the callee should delibately design, not something the callee should use or leave off on a whim. It's a philosophical difference, but one that leads to different designs.

It was my mistake to put a syntax suggestion in the original post. I should have stopped short of that. Because it has moved the focus from where I was aiming.


I thought it was evident from the code, but I will labour the point:


func X( a:Int,  b:Int) {
    print(a,b)
}


a and b are equivalent to one another. This is the same deal in every programming language I've ever come across.


But at the call site,

X(1, b:2)


b requires different treatment from a.


The first parameter is treated differently from the others.


I am appalled by the ugliness of this. It flies in the face of intelligent language design. It creates an unwelcome speed-bump to every Swift programmer.


I retract my original assertion, in retrospect this is what I should've written:


"I would personally prefer all cases to work, although I can see an argument for requiring consistency at the call site. However, whichever choice is made, it should go without saying that the equivalence of the parameters should be reflected in the syntax at the call-site."

But if you instead define it this way:


func X(a a:Int,  b:Int) {
    print(a,b)
}


you call it that way:


X(a:1, b:2)


Now, the asymmetry has moved to the function definition. However, you can always define it like this:


func X(a a:Int, b b:Int) {
    print(a,b)
}


and it will still be called in the same way (but the compiler issues a warning that it is unnecessary).


Swift places a heavier burden on the function definer than the function caller even in other cases, e.g. types always have to be specified when defining a function. This is in line with that. If you want control over how a function should be called, you have that flexibility, but you have to write more in the definition.


I disagree with many design decisions in Swift, but I think it is helpful to try to understand the options available to you before you feel entitled. I am not without sin in this case.


I think the reasons for why the defaults are the way they are have been explained adequately above.

'That "named parameter mess" reminds me of C++, where everything breaks down into a zillion edge cases.'


Actually, Swift 2 fixes the "zillion edge cases" by harmonising the static/global function parameter rules with the Swift 1 (and Objective-C) method rules. They're simplying further by removing the "#" rule: you must duplicate the first parameter name instead (duplication of the others is never useful now). Initialisers still use the "all parameters are named" rule, but that's still in harmony with Objective-C conventions.


My personal code style typically includes a first parameter name (just like initialisers). I love remove(element:) much more than removeElement(_:). If it were for me, I'd apply external parameter names everywhere by default (and require _ whenever the user doesn't want it). But that's not very close to Objective-C.

Regarding your last paragraph, to what are you alluding?


I can't find any explanation/justification of why the default behaviour is the way it is.


I suspect it is to do with bridging betweenSwift and Objective-C


[someObj someMethodWith:a andB:b etc...] <-> someObj.someMethod(a, andB:b, ...)


But I just can't quite see it.

It might be easier to see with this slight correction to your example:


[someObj someMethodWith:a  andB:b  ...]
 someObj.someMethodWith(a, andB:b, ...)


Ie, as in Objective-C, the convention is that the name of, or preposition refering to, the first parameter is fused together with the initial part of the method/function-name. And The Swift documentation says for example:


Methods in Swift are very similar to their counterparts in Objective-C. As in Objective-C, the name of a method in Swift typically refers to the method’s first parameter using a preposition such as

with
,
for
, or
by
, / ... / .


The similarity in naming conventions between the languages is perhaps even clearer in this example of an existing method:

[NSObject copyScriptingValue:someValue, forKey:someKey  withProperties:someProperties]// In Objective-C
 NSObject.copyScriptingValue(someValue, forKey:someKey, withProperties:someProperties)// In Swift


The documentation also says that Swift "adopts the readability of Objective-C's named parameters".


So, following the convention we would for example write this:

extension SignedNumberType {
    func clampedBetween(minValue: Self, and maxValue: Self) -> Self {
        return self < minValue ? minValue : self > maxValue ? maxValue : self
    }
}
let a = 12.34
let b = a.clampedBetween(20.0, and: 30.0)

rather than this:

extension SignedNumberType {
    func clamped(between minValue: Self, and maxValue: Self) -> Self {
        return self < minValue ? minValue : self > maxValue ? maxValue : self
    }
}
let a = 12.34
let b = a.clamped(between: 20.0, and: 30.0)


And, as others have pointed out, the reasons for why any (external) parameter names are not optional when calling a function/method can be seen by looking at these two standard library functions:

func stride<T : Strideable>(from start: T, through end: T, by stride: T.Stride) -> StrideThrough<T>
func stride<T : Strideable>(from start: T,      to end: T, by stride: T.Stride) -> StrideTo<T>

(The only thing that makes them two different functions is "through" vs "to". (And also the types of their return values ... but anyway, you get the point.))


I can see the reasons for all this, and I have accepted the special rule for the first parameter's external name. But I agree that the documentation could do a better job explaining it.

Many thanks for the links, information and explanations provided.


In light of all of the above, I would like to put forward a motion begging the language designers to rework this behaviour.


So that:


func X(    a:int,     b:int)   <-->   X(  a:1,   b:2)
func X(foo a:int, bar b:int)   <-->   X(foo:1, bar:2)
func X(  _ a:int,   _ b:int)   <-->   X(    1,     2)


That would be a mathematically elegant syntax.

Someone would be able to look at those three lines and instantly understand everything. No special treatment for the first parameter.

And the third line follows from the second, as a special case.


One wouldn't even require prose to explain, it speaks for itself!


Swift is becoming open source. This means it is going to become decoupled from Objective-C APIs.

As far as I can see the only defence for this syntax is consistency with Objective-C's [myObj myMethodWith:myFirstArg and:...] method syntax.

It is making a dent in Swift to match a bump in Obj-C.

If Swift and Obj-C are to be joined at the hip, there is some argument for this. I don't agree with it, but I can see there is a potential argument.


But if Swift is to become decoupled, standing on its own, it doesn't look or feel right. It is not pretty. It is not intuitive. It is not symmetrical. It's a speed bump everyone is going to hit. Everyone will ask "but why?" to which the answer will be at best "Oh some Objective-C legacy cruft...", and over time more likely "I have absolutely no idea, it is just some additional thing that you have to remember to do!"


We have to strike while the iron is hot, otherwise this deformity will become a permanent feature.


I have heard of a tribe in Africa where the desirability of a woman is determined by the degree to which she can extend her neck, through adding hoops.

For better or for worse I am only able to see things as they appear.

And this doesn't look right.


π

“We have to strike while the iron is hot, otherwise this deformity will become a permanent feature.”


We demonstrably don't have to do this, since it was already changed between Swift 1 and Swift 2, and could similarly change in future. As they went to the effort of making this change and implementing the migration, you can assume that there was significant discussion that included the option you're campaigning for. So I don't really like your chances of convincing them to change it, but you might get someone to explain the rationale.

Especially with many developers liking this style of named parameters with the first unnamed.

This is a good point. The way it currently works is not obvious at all.

@JawBroken, Swift 1 -> 2 occurred over the first year of Swift's public life. I don't have stats on the percentage adoption rate for Swift, but from what I read it appears that transition from Objective-C is gradual.


Open-source-ing the language will massively increase the adoption rate. As the Swift codebase increases, it will become progressively more labourious to update existing code. As versions roll out, core changes will become less welcome.


That's what I mean by "strike while the iron is hot".


Look at Python 2 -> 3. That has split the Python community in two. The problem is that 2.x was so successful, the work required updating to 3.x became impractical and as a consequence a lot of people just didn't do it. This has significantly reduced the desirability of Python.


@Tia Lapis, that style may be preserved, without having to bake it into the language! The syntax I suggested would allow that style. Just, if you wish to use that style, you must specify in your function definition that the first parameter is unnamed:


func f( _ a: int,  b: int) {...} // call f(1, b:2)


Keep the language simple and clean! A competent programmer familiar with a handful of other languages should be able to just look at Swift code and see what is going on. That is an indication of good design. They should not be required to know any weird & questionable conventions. That just detracts from the appeal of the language.


In fact one could go further; if a language is well-designed, an intelligent non-programmer should be able to look at code and figure out what is going on. That would be a mark of a well-designed language. If we can take a step towards achieving this without conceding... anything in fact... surely we should do it!

Swift wants to be the next safe (!) language. In order to make it a safe language you sometimes have to remove assumptions and add clarity to code. I can think of a number of reasons why this notation is used: - easier interopability with Obj-C - if you have a method with one argument it is usually easy to determine what the argument means, if you have multiple, it becomes a lot harder. Chris Lattner mentioned in one of the WWDC talks the horror of unprefixed booleans e.g. remove(tag, true, true) - Swift allows function overloading both on argument types and return types. The arguments prefixes can add a little extra clarity to which function you intended to call (e.g. beforeLayer vs afterLayer mentioned above) With every new language you'll learn there will be a few things you like and a couple of things you miss from other languages. Especially in Ruby you can write a lot of code a lot shorter than in Swift, but it doesn't necessarily make it better code. I kinda like Swift's attitude towards a single style.

@Tia Lapis, that style may be preserved, without having to bake it into the language! The syntax I suggested would allow that style. Just, if you wish to use that style, you must specify in your function definition that the first parameter is unnamed:


func f( _ a: int, b: int) {...} // call f(1, b:2)


I wholeheartly disagree - The good thing is the FORCED nature to use this style. Without force the same sucking coders writing junk in C++ will just do the same in Swift. This has to be prevented. If it annoys C++ fans, more the better.

Named parameter HORROR
 
 
Q