为什么参数标签在这个Swift代码中变得无关紧要?



我对Swift文档的理解是参数标签,而不仅仅是参数数据类型,是编译时函数签名固有的。因此,编译器理解这两个版本的f具有不同的类型:它们都接受一个String参数,但标签不同。

func f(foo str : String) -> String { "foo" }
func f(bar str : String) -> String { "bar" }
f(foo: "hi") // "foo"
f(bar: "hi") // "bar"

然而,我对下面的行为感到困惑。gh也有不同的类型,因为它们有不同的参数标签。但是,即使缺少参数标签,这段代码也可以编译和运行:

func f(foo str : String) -> String { "foo" }
func f(bar str : String) -> String { "bar" }
func g(foo str : String) -> String { f(foo: str) }
func h(bar str : String) -> String { f(bar: str) }
(0 < 100 ? g : h)("hello") // "foo" (why?)

但是,如果我们引入第二个具有不同参数标签的h,代码将无法编译,因为现在我们有两种冲突类型的h:

func f(foo str : String) -> String { "foo" }
func f(bar str : String) -> String { "bar" }
func g(foo str : String) -> String { f(foo: str) }
func h(bar str : String) -> String { f(bar: str) }
func h(foo str : String) -> String { f(foo: str) }
(0 < 100 ? g : h)("hello") // ambiguity error

我的问题是:为什么在第二个例子中,Swift编译器对类型检查采取了更宽松的观点?我理解没有类型歧义。但是这种行为似乎仍然违反了Swift关于表达式返回类型需要匹配的非常强的策略。

如果我们认为参数标签是类型签名的一部分,那么像0 < 100 ? g : h这样的表达式甚至不应该编译,因为gh有不同的参数标签,因此不同的类型。

事实:Swift中的任何函数都可以简化为闭包,当它们被传递时,它们实际上就是闭包。fgh都可以简化为(String) -> String类型的闭包。

接下来,你的(some_computation)("hello")代码实际上做的是告诉编译器你想创建一个匿名函数(又名闭包),你用"hello"论点。

因此,(0 < 100 ? g : h)("hello")指示Swift编译器将gh函数转换为(String) -> String类型的闭包。闭包没有参数标签,因此任何带标签的函数都可以匹配。

然而,在有问题的代码片段中,编译器不能再唯一地识别哪个h";重载"了。使用。因此,它退出了。

如果您想要明确匹配h函数之一,那么您需要完全限定它:

(0 < 100 ? g : h(foo:))("hello")

规则对成员函数也是一样的,只要编译器可以决定传递哪个闭包,它就会匹配函数名,而不必指定它的完整名称。

要点是在Swift中你不是循环函数,而是循环闭包。没有办法指定变量、函数参数或属性来拥有带标签的参数。

最新更新