Confusion over Argument labels

Consider this code:

	func createManufacturer(ident: String, plugin: String, pluginType: String) -> String {
		let map = getReplacementsMap(plugin: plugin, pluginType: pluginType);
...
...
	func getReplacementsMap(plugin: String, pluginType: String) -> [String: String] {
		var map = [String: String]()

I previously had the call to getReplacementsMap like this:
let map = getReplacementsMap(plugin: String, pluginType: String);
but Xcode was complaining that Cannot convert value of type 'String.Type' to expected argument type 'String'
I've fixed it so that Xcode doesn't throw a wobbly, but I don't understand the reasoning behind this. I gather that String.Type is a meta-type but I don't understand wht Xcode thought I was using them!

If you call the function simply with:

let map = getReplacementsMap(plugin: "hello", pluginType: "this is a string");

then it works fine: plugin: is the label for the argument, and "hello" is the value.

In your case, you have a variable containing a string you want to use, so you need to replace "hello" with this variable. The variable might have the same name as the label, but you still need to spell both:

let map = getReplacementsMap(plugin: plugin, pluginType: "this is a string");

If instead you put the type, then the compiler assumes you're trying to pass a metatype object to the function, and you get the error you got:

let map = getReplacementsMap(plugin: String, pluginType: "this is a string");

Note that this is not the correct way to spell the metatype either (you should use String.self for that), but the compiler isn't sure of what you're trying to do and takes a guess in its error message (a wrong one in this case).

1 Like

Xcode thought you are doing this:

func createManufacturer(ident: String, plugin: String, pluginType: String) -> String {
    let map = getReplacementsMap(plugin: String.self, pluginType: String.self);
}

for it to work the "getReplacementsMap" should've been typed:

func getReplacementsMap(plugin: String.Type, pluginType: String.Type) -> [String.Type: String.Type] {
}

(although that can't work anyway because String.Type would have to be Hashable and it is not)

String.Type is a metatype of type String.

The following table illustrates the relationship:

let x: Int = 0 // or Int(), etc
let y: Int.Type = Int.self
let z: Int.Type.Type = Int.Type.self
let t: Int.Type.Type.Type = Int.Type.Type.self

For my Rust / Java / Kotlin / C# / most_other_programming_languages colleagues Swift's "external labels" is often a point of confusion.

  • "why can't we call it like so?"
getReplacementsMap("plugin", "pluginType")
  • "if I have to specify those labels anyway why can't I reorder them?"
getReplacementsMap(pluginType: "pluginType", plugin: "plugin")
  • "are you telling me that the name of the functions is not "getReplacementsMap" but:
getReplacementsMap(plugin:pluginType:)
  • "what are those funny underscores for?"
func getReplacementsMap(_ plugin: String, _ pluginType: String) { ... }

The relevant section in the language doc is here.

Aha! I think I get it now. So the motivation for the way that Swift does it, is primarily to match the source with the destination, where most/all other languages put specifying the type of the parameter as the crucial thing to get right. I'm not sure I've worded it that well...
Thanks for the explanations! Would I be correct in guessing that I am not the first person with whom you have had this conversation? :wink:

1 Like

We were all beginners once. I've been on your side of the conversation before.

6 Likes

in a lot of C++ codebases i have worked on, it’s common to see things like

DoSomething(/* timestamp= */ created_, /* name= */ id_);

swift just formalizes this into the language so you don’t have to insert label comments everywhere.

2 Likes

Good attitude! That's SO refreshing to hear compared to SOme other forums I could mention... :wink:

5 Likes