Swift Generics:比泛型更专业?

时间:2014-08-12 22:18:37

标签: xcode generics swift

XCode 6:Beta 5:

目标:

我正在尝试为语义兼容但不共享(或似乎共享)足够协议的类型编写通用代码,以便将我的泛型基于共享协议的子集。到目前为止,我还没有找到解决方案,并且想知道我错过了什么或者是否是语言的限制 - 任何见解都值得赞赏。

问题:

我有一些函数只是按类型而不是语义而不同,看起来非常适合泛型。我所遇到的问题是,从我所知道的情况来看,Swift看起来像是仿制药的解析时绑定,如果可能存在问题则失败,而不是实际存在问题时。

示例:

在一个人为的例子中考虑以下通用函数:

func defVal<T where T:FloatingPointType, T:FloatLiteralConvertible>(T.Type) -> T {
    return 0.0
}

func defVal<T:IntegerLiteralConvertible>(T.Type) -> T {
    return 0
}

请注意,我提供的函数应该跨越整数和浮点数的情况,并且故意不想提供与我无关的所有可能变体的详尽列表。

然后我想要定义跨越类型的通用代码 - 在本例中,int和float类型。请注意,即使没有任何调用它的代码,此代码也无法编译:

func doSomethingGeneric<T>(t:T) -> [T]
{
    let a = defVal(T) // Type 'T' does not conform to protocol FloatLiteralConvertible
    let b = a * a   // works
    return [t]
}

在我的回忆中,这将在C ++中编译,直到你用不兼容的类型调用它,此时编译器会捕获它。

我还尝试了其他减少效用的变体:

func doSomethingWithFloats<T
    where T:FloatingPointType, T:FloatLiteralConvertible>(t:T) -> [T]
{
    let a = defVal(T) // works
    let b = a * a // T is not convertible to UInt8
        // - need a floating point arithmetic type?

    let c = -a          // T is not convertible to Float
    let f:Float = -a    // T is not convertible to Float
    return [t]
}

鉴于Swift提供协议作为分组具体实例(专业而非通用)的方式,我编造了一个protocludge:

protocol Initializable {}
extension Float : Initializable {}
extension Double : Initializable {}
extension CGFloat : Initializable {}

func doSomethingWithInitializable<T:Initializable>(t:T) -> [T]
{
    let a = defVal(T) // Type 'T' does not conform to protocol FloatLiteralConvertible
    let b = a * a   // works
    return [t]
}

请注意,即使在所有Initializable类型的集合中实现FloatLiteralConvertible,也会失败。换句话说,Swift似乎过早地绑定泛型类型,并将泛型类型视为专门的具体实例,而不是更大的模式,可以在链中进一步编译。此外,请注意,虽然我可以从FloatLiteralConvertible派生,但这将阻止我支持int类型等。如果有一个共同的ArithmeticType协议,可以想象可以工作,但我没有看到任何类型。这就是问题的症结所在 - 虽然int和float类型在语义上都是兼容的(具有相同的数学规则),但是没有适用于这两者的通用协议。

总而言之,如何编写类型在语义上兼容的泛型函数,但是没有足够的跨越协议来按协议过滤(在这种情况下 - FloatingPointType不实现IntegerArithmeticType,但在语义上能够算术)。

提前致谢。

1 个答案:

答案 0 :(得分:6)

与C ++不同,Swift不会通过替换呼叫站点上的具体类型并制作非泛型副本来处理泛型(至少在一般情况下它不会 - 作为允许的优化,但我离题了)

Swift通过将描述每次调用的实际类型的元数据信息传递到一个主函数来处理泛型,然后使用元数据提供的入口点来操作对象

在您的示例中,Initializable不提供任何操作,因此当编译器尝试执行defVal(T)时,它不知道该怎么做(它如何确保您的类型存在defVal重载?)< / p>

你想要做的是将initVal定义为Initializable协议上的静态函数,然后在扩展中实现它,然后Swift将知道T.defVal()意味着类似于

metadata[Initializable]->defVal(metadata)

哦,既然您正在尝试执行*(T,T),您可能还想制作一个Multipliable协议,然后将您的T键入为

<T: protocol<Initializable,Multipliable>>