Swift Initializer Generics


(Tyler Cloutier) #1

Hi everyone,

I was wondering if there was a trick to get the following thing accomplished within the framework of Swift generics. I am using Surge <https://github.com/mattt/Surge>, a very nice and useful wrapper for the Accelerate framework. The problem is that I would like to extend the functionality of Surge in my own graphics library to include geometric constructs and I’ve run into an issue.

In order to use the appropriate Accelerate function call, Surge duplicates each function on the parameter types, either Float or Double. Ideally, I think there should be a better way to accomplish this with Swift generics, but perhaps that is a separate discussion. As an example consider dot product of Arrays (Vectors).

public func dot(x: [Float], y: [Float]) -> Float {
    precondition(x.count == y.count, "Vectors must have equal count")

    var result: Float = 0.0
    vDSP_dotpr(x, 1, y, 1, &result, vDSP_Length(x.count))

    return result
}

public func dot(x: [Double], y: [Double]) -> Double {
    precondition(x.count == y.count, "Vectors must have equal count")

    var result: Double = 0.0
    vDSP_dotprD(x, 1, y, 1, &result, vDSP_Length(x.count))

    return result
}

Consider, now, that I want to make a new function that uses one of these Surge functions. Am I correct in saying that I must define two functions, one for Float and one for Double?

func normalized(x: [Float]) -> [Float] {
    return x / sum(x)
}

func normalized(x: [Double]) -> [Double] {
    return x / sum(x)
}

Is there no way to get this done with generics? Writing the function with generic parameters yields the following error:

func normalized<T where T: FloatingPointType, T: FloatLiteralConvertible>(x: [T]) -> [T] {
    return x / sum(x) // Error: Cannot invoke 'sum' with an argument list of type '([T])'
}

Let’s assume, however that I go ahead and create two ‘normalized’ functions. I then want to create a new struct type call Plane. I would like to be able to construct a Plane using a normal vector and a point. Like so:

struct Plane<T where T: FloatingPointType, T: FloatLiteralConvertible> {
    let normal: [T]
    let distance: T
    
    init(normal: [T], point: [T]) {
        self.normal = normalized(normal) // Error: Ambiguous reference to member 'normalized'
        self.distance = dot(normal, y: point) // Error: Cannot invoke 'dot' with an argument list of type '([T], y: [T])'
    }
}

Yet as you can see there are still errors. In fact, as far as I can figure there is no way I can accomplish this last bit, no matter how much duplication I’m willing to tolerate.

Any help that anyone can provide would be much appreciated.

Thanks!

Tyler