这种类型错误的原因是什么?

时间:2016-06-16 14:35:52

标签: swift types

两个问题:

  1. 为什么这段代码无法编译?我相信(但我不是100%肯定,我可能犯了一个错误),它的类型是正确的。

  2. 错误消息是什么意思?我很困惑为什么预期的参数类型是_ -> _(或者在这种情况下我只是不知道_ -> _的含义)。这个问题的目标是学习如何正确诊断此错误消息,如果我将来再次遇到它。

  3. 代码:

    此代码无法使用错误消息进行编译"无法转换类型' A - >的值。 B'预期参数类型' _ - > _':

    class ZipList<A> {
        let xs: [A]
        init(xs: [A]) {
            self.xs = xs
        }
        func map<B>(f: A -> B) -> ZipList<B> {
            return ZipList(xs: self.xs.map(f))
        }
    }
    

    enter image description here

    其他信息:

    起初,我认为问题在于类型推断,所以我尝试明确地写出类型,但也失败了:

    enter image description here

    enter image description here

    但是,这个编译得很好(与原始map版本的唯一区别是传递给<B>初始值设定项的ZipList):

    func map4<B>(f: A -> B) -> ZipList<B> {
        return ZipList<B>(xs: self.xs.map(f))
    }
    

1 个答案:

答案 0 :(得分:6)

问题在于,当你引用它时,当你没有明确地提供ZipList的泛型参数类型时,编译器会尝试为你推断它 - 它没有&总是变得正确。

由于您已经在ZipList<A>类中,当您省略通用参数时,编译器会尝试将ZipList推断为ZipList<A>(请参阅this question有关此行为的更多信息)。

因此,它现在期望在[A]初始值中输入ZipList(xs:_),这意味着地图函数被推断为A -> A,您正在尝试将A -> B传递给,导致类型不匹配(这就是为什么f会突出显示为错误中的问题。)

如果您将示例简化为只在init()上调用ZipList而不提供参数,那么您会看到更有用的错误消息:

class ZipList<A> {

    init() {}

    func map<B>() -> ZipList<B> {
        // error: Cannot convert return expression of type 'ZipList<A>' to 'ZipList<B>'
        return ZipList() 
    }
}

编译器完全忽略map()方法返回的显式类型注释这一事实是一个错误,由SR-1789跟踪。乔丹·罗斯在报告评论中描述的原因是:

  

似乎我们急切地假设参数与self相同。 (这通常是一个特征,但不会妨碍其他推断。)

您已经找到的解决方案是在创建新实例时显式声明ZipList的通用参数类型:

return ZipList<B>(xs: xs.map(f))

这会强制泛型参数的类型为B,因此阻止Swift错误地推断它,允许map函数解析。

至于错误消息&#34; 无法转换类型&#39; A - &gt;的值B&#39;预期参数类型&#39; _ - &gt; _ &#34;意味着,在这种情况下,_只是指编译器无法解析的泛型类型(我知道这不是一个有用的错误消息)。因此,所有编译器都告诉你,它期望一个函数接受一个未知类型的输入,并返回相同的类型。

在诊断这些错误消息时,将表达式拆分为多个子表达式并检查每个类型的类型以尝试查找不匹配通常会有所帮助。它还可以帮助您开始简化示例(例如在init()方法中使用init(xs:[A])而不是map),直到您遇到更有用的错误消息。