一尘不染

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

swift

这是对问题的跟进:协议func返回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),我不知道那是什么意思。我也尝试过添加requiredfuncmutated()但显然required只允许这样做inits。有什么办法可以使它正常工作吗?


阅读 246

收藏
2020-07-07

共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,如您的自动完成功能所示。和以前C一样,Self除非您可以保证没有子类(C不是要么final是struct),否则和之前一样。除非您使用,否则Swift类型是在编译时而不是运行时解析的dynamicType

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

    let mutated = copy(self)

它指出,它copy在其参数类型上是通用的,并且必须copy 在编译时 构造一个版本
进行调用。没有类型Self。它只是一个占位符,必须在编译时解决。self在此词汇范围内的类型是C。如此构造copy<C>。但是,如果您将其子类化C,则可能是错误的函数(在这种情况下,可能是错误的)。这与以下内容密切相关
:

类型autocomplete表示的事实(C)不是CSwift函数和元组如何工作的次要副作用,而且定期出现,但我还没有遇到真正重要的案例。类似的Swift函数func f(x: Int, y:Int)实际上没有两个参数。它具有一个类型为2的2元组参数(Int, Int)。这个事实对于currying语法的工作方式非常重要(有关Swift中的curring的更多信息,请参见Swift编程语言)。因此,当您进行专业化处理时copy,可以使用类型为1的元组对其进行专业化处理(C)。(或者,编译器可能只是尝试将其作为各种尝试之一,而这只是它报告的结果。)在Swift中,任何值都可以被琐碎地交换为相同类型的1元组。因此,的回报copy实际上是的1元组C,书面的(C)。我怀疑Swift编译器会随着时间的推移改进其消息以消除多余的括号,但这就是为什么它们有时会出现的原因。

2020-07-07