【问题标题】:Swift protocol and return types on global functionsSwift 协议和全局函数的返回类型
【发布时间】:2014-09-05 23:03:02
【问题描述】:

这是问题的后续: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。有什么办法让它工作吗?

【问题讨论】:

    标签: swift


    【解决方案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() -&gt; Self { return copy(self) },要么必须创建一些实现此方法的具体类,C 最终继承自。这是目前 Swift 的一个基本限制。我同意 Mattt 对可能解决方案的诊断,并怀疑将来可能会添加某种特征系统,可能与 Scala 类似。

    值得关注 Mattt 的评论,“所有这些都凸显了 Swift 中方法和函数之间的显着张力。”这是另一种说法,面向对象范式和函数范式之间存在张力,在它们之间移动可能会产生一些脱节。语言试图用各种特性来掩盖这个问题,但是具有消息和属性的对象与具有数据和组合器的函数之间存在重要差异,并且“两全其美”有时会产生一些粗糙的边缘。

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


    对于您的评论,您可能认为mutatedSelf 类型。但它的类型为C,正如您的自动完成所指示的那样。和以前一样,CSelf 不同,除非您可以保证没有子类(Cfinal 或结构)。 Swift 类型在编译时解析,而不是在运行时解析,除非你使用 dynamicType

    更具体一点,Swift 看这行:

        let mutated = copy(self)
    

    它注意到copy 在其参数类型上是通用的,它必须在编译时构造一个copy 的版本 才能调用。没有类型Self。它只是一个占位符,必须在编译时解析。在这个词法范围内self 的类型是C。所以它构造了copy&lt;C&gt;。但是,如果您将C 子类化,这可能是错误的函数(在这种情况下,会是)。这与:https://stackoverflow.com/a/25549841/97337 密切相关。

    类型自动完成表示 (C) 而不是 C 的事实是 Swift 函数和元组工作方式的一个次要副作用,并且经常出现,但我还没有遇到真正重要的情况.像 func f(x: Int, y:Int) 这样的 Swift 函数实际上没有两个参数。它有一个(Int, Int) 类型的二元组参数。这一事实对于柯里化语法的工作方式很重要(有关 Swift 中柯里化的更多信息,请参阅 Swift 编程语言)。因此,当您专门化 copy 时,您使用类型为 (C) 的 1 元组专门化它。 (或者可能,编译器只是试图将其作为各种尝试之一,而这正是它所报告的。)在 Swift 中,任何值都可以轻松地交换为相同类型的 1 元组。所以copy的返回实际上是C的1元组,写成(C)。我怀疑 Swift 编译器会随着时间的推移改进其消息以删除多余的括号,但这就是它们有时会出现的原因。

    【讨论】:

    • 好的,所以对一切都使用初始化程序。知道了。再次感谢!
    • 我想我的问题是,如果copy(T) -&gt; T 返回调用它的类,为什么原始代码不起作用?在mutated() 函数中它应该返回Self 对吗?即使有更好的方法,我仍然对 Swift 的工作原理感到好奇。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-06
    • 1970-01-01
    相关资源
    最近更新 更多