Search code examples
swiftfloating-pointswift3swift-extensions

Extension of Array of FloatingPoint Elements in Swift 3.0


How can I compactly write an extension of an Array in Swift 3.0 which works for both Float and Double Element types?

The following doesn't work:

extension Array where Element: FloatingPoint
{
    public func multiply(mult: Double) -> [Double] {
        return self.map{Double($0) * mult}
    }
}

With the error being

Unable to infer closure type in the current context

Why can't the closure type be inferred? Is this a current limitation of the compiler, or is there a good reason why the closure type can't be inferred?

A more explicit version doesn't work either:

extension Array where Element: FloatingPoint
{
    public func multiply(mult: Double) -> [Double] {
        return self.map{(x: Element) -> Double in Double(v: x) * mult}
    }
}

With the error this time being

Ambiguous reference to member '*'

Where again I'm not sure of the reason for this error.


Solution

  • Logically, your extension should work by multiplying an array of homogenous floating-point types with a value of the same type, and returning an array of that type. You can simply express this with an argument of type Element, and a return of [Element]:

    // this could also just be an extension of Sequence
    extension Array where Element : FloatingPoint {
    
        public func multiply(by factor: Element) -> [Element] {
            return self.map { $0 * factor }
        }
    }
    

    Therefore for a [Float], this would accept an argument of type Float and return a [Float].

    However, what if I indeed want to return an array of Double?

    I do not believe it's possible to construct a Double from an arbitrary FloatingPoint (or even BinaryFloatingPoint) conforming instance, as neither protocol (although they do require implementation of various aspects of the IEEE 754 specification) actually defines the precise encoding of the conforming type.

    However, if you really want this, you could just write two overloads – one for Float elements and one for Double elements:

    extension Sequence where Iterator.Element == Float {
    
        public func multiply(by factor: Double) -> [Double] {
            return self.map { Double($0) * factor }
        }
    }
    
    extension Sequence where Iterator.Element == Double {
    
        public func multiply(by factor: Double) -> [Double] {
            return self.map { $0 * factor }
        }
    }
    

    Alternatively, if you plan on making this work with a broader range of types, you can use a protocol in order to define a requirement that allows conforming types to express their value as a Double:

    protocol ConvertibleToDouble {
        func _asDouble() -> Double
    }
    
    extension Float : ConvertibleToDouble {
        func _asDouble() -> Double { return Double(self) }
    }
    
    extension Double : ConvertibleToDouble {
        func _asDouble() -> Double { return self }
    }
    
    extension Sequence where Iterator.Element : ConvertibleToDouble {
    
        func multiply(by factor: Double) -> [Double] {
            return self.map { $0._asDouble() * factor }
        }
    }