Swift协议和全局函数的返回类型

时间:2014-09-05 23:03:02

标签: swift

这是问题的后续问题:Protocol func returning Self。协议如下:

protocol Copyable {
    init(copy: Self)
    func copy() -> Self
}

以下工作正常但copy()函数对于每个实现都完全相同,即

func copy() -> Self {
   return self.dynamicType(copy: self)
}

根据这个http://nshipster.com/swift-default-protocol-implementations/,我尝试了一个全局函数

func copy<T : Copyable>(makeCopy: T) -> T {
    return makeCopy.dynamicType(copy: makeCopy)
}

然而,当它在实现以下协议的类中被调用时

protocol Mutatable : Copyable {
    func mutated() -> Self
}

class C : Mutatable {

    var a = 0

    required init(_ a: Int) {
        self.a = a
    }

    required init(copy: C) {
        a = copy.a
    }

    func mutated() -> Self {
        let mutated = copy(self)

        mutated.a++

        return mutated // error: 'C' is not convertible to 'Self'
    }

}

我收到了错误,如上所述。当我输入mutated自动填充功能时,mutated显示为(C),我不知道这意味着什么。我还尝试将required添加到func mutated(),但显然required仅允许inits。有什么方法可以让它发挥作用吗?

1 个答案:

答案 0 :(得分:1)

此问题的形式与复制问题相同,也是同一解决方案。使变异成为初始化者而不是方法。

protocol Copyable {
  init(copy: Self)
}

protocol Mutatable : Copyable {
  init(byMutating: Self)
}

class C : Mutatable {
  var a = 0

  required init(_ a: Int) {
    self.a = a
  }

  required init(copy: C) {
    a = copy.a
  }

  required convenience init(byMutating: C) {
    self.init(copy: byMutating)
    self.a++
  }
}

// These are purely for convenience
func copy<T : Copyable>(x: T) -> T {
  return x.dynamicType(copy: x)
}

func mutated<T: Mutatable>(x: T) -> T {
  return x.dynamicType(byMutating: x)
}

但是要重申Mattt在链接文章中的观点,你可以非常方便地使用C(copy: x)语法,并且可以非常方便地使用copy(x)语法,并且总是x.dynamicType(copy: x)。但是如果没有烦人的工作,你就不能拥有x.copy()语法。您要么必须在每个类中复制func copy() -> Self { return copy(self) },要么必须创建一个实现此方法的具体类,C最终继承自。这是目前Swift的一个基本限制。我同意Mattt对可能解决方案的诊断,并怀疑将来可能会添加某种特征系统(可能与Scala一样)。

值得关注的是Mattt的评论,“所有这些都凸显了Swift中方法和功能之间的紧张关系。”这是另一种说法,即面向对象的范式与功能范式之间存在紧张关系,在它们之间移动可能会产生一些断开连接。语言试图通过各种功能来解决这个问题,但是具有消息和属性的对象与具有数据和组合器的函数之间存在重要差异,并且“充分利用两个世界”有时会产生一些粗糙的边缘。

在将Swift与其他语言进行比较时,很容易忘记v0.9和v2.11之间存在很大差异。我们用我们最喜欢的语言认为理所当然的许多事情都不存在于他们的v1中。


对于您的评论,您可能会认为mutated属于Self类型。但它的类型为C,因为您的自动填充功能表明了这一点。和以前一样,CSelf不同,除非您可以保证没有子类(Cfinal或结构)。 Swift类型在编译时解析,而不是运行时,除非您使用dynamicType

更具体一点,Swift看这一行:

    let mutated = copy(self)

它指出copy在其参数类型上是通用的,它必须在编译时构建copy 的版本才能调用。没有类型Self。它只是一个占位符,必须在编译时解决。此词法范围中self的类型为C。所以它构建了copy<C>。但是如果你继承了C,那么这可能是错误的功能(在这种情况下,将是)。这与https://stackoverflow.com/a/25549841/97337非常密切相关。

类型自动填充说(C)而不是C这一事实是Swift功能和元组如何工作的一个小副作用,并且经常出现,但我还没有遇到一个案例真正重要的地方。像func f(x: Int, y:Int)这样的Swift函数实际上没有两个参数。它有一个类型为(Int, Int)的2元组参数。这个事实对于currying语法的工作原理很重要(有关Swift中的currying的更多信息,请参阅Swift编程语言)。因此,当您对copy进行专门化时,您会使用类型为(C)的1元组进行专门化。 (或者可能,编译器只是尝试将其作为各种尝试中的一种,而这只是它报告的那种。)在Swift中,任何值都可以简单地交换为相同类型的1元组。因此,copy的返回实际上是C的1元组,写为(C)。我怀疑Swift编译器会随着时间的推移改进其消息以删除无关的括号,但这就是它们有时出现的原因。