为什么在已定义所有类型时需要通用?

vor*_*tek 11 generics swift

练习是编写我自己的map()函数Collection(不使用任何功能原语,如reduce()).它应该处理这样的情况:

func square(_ input: Int) -> Int {
            return input * input
        }
let result = input.accumulate(square) // [1,2,3] =>  [1,4,9]
Run Code Online (Sandbox Code Playgroud)

我的第一次尝试是:

extension Collection {
    func accumulate(_ transform: (Element) -> Element) -> [Element] {

        var array: [Element] = []
        for element in self {
            array.append(transform(element))
        }
        return array
    }
}    
Run Code Online (Sandbox Code Playgroud)

这在游乐场中运行良好,但无法针对测试构建,给出错误:

Value of type '[Int]' has no member 'accumulate'
Run Code Online (Sandbox Code Playgroud)

解决方案是将accumulate方法泛化:

extension Collection {
    func accumulate<T>(_ transform: (Element) -> T) -> [T] {

        var array: [T] = []
        for element in self {
            array.append(transform(element))
        }
        return array
    }
}    
Run Code Online (Sandbox Code Playgroud)

我认识到通用版本限制较少(不要求转换返回相同的类型),但鉴于测试不需要这种通用性,为什么编译器?

出于好奇我试过:

extension Collection {
    func accumulate<Element>(_ transform: (Element) -> Element) -> [Element] {

        var array: [Element] = []
        for element in self {
            array.append(transform(element))
        }
        return array
    }
}    
Run Code Online (Sandbox Code Playgroud)

这引发了令人着迷的构建错误:'(Self.Element) -> Element' is not convertible to '(Element) -> Element'append()声明中.

所以编译器(当然)知道第一个Element是Self.Element,但不会将其他Element类型视为相同.为什么?


更新:

根据答案,似乎拒绝第一个版本是一个编译器错误,修复在XCode 9.2(我在9.1).

但我仍然想知道是否在

func accumulate(_ transform: (Element) -> Element) -> [Element]
Run Code Online (Sandbox Code Playgroud)

它会看到两种类型(Self.ElementElement)或认识到它们是相同的.

所以我做了这个测试:

let arr = [1,2,3]
arr.accumulate {
    return String(describing: $0)
}
Run Code Online (Sandbox Code Playgroud)

果然,得到了预期的错误: error: cannot convert value of type 'String' to closure result type 'Int'

所以正确的答案是:只要不存在重载名称的泛型类型,编译器就会将对Element的引用视为相同.

奇怪的是,这成功了:

[1,2,3].accumulate {
    return String(describing: $0)
}
Run Code Online (Sandbox Code Playgroud)

PS.感谢大家的投入!赏金是自动授予的.

vor*_*tek 8

原始构建错误是编译器错误.实际上,编译器识别出所有实例Element都是相同的,因为Element在函数上没有作为泛型类型重载.