1

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.

6
  • @Hamish Doing what you suggest works, thanks! That might be good enough. However, what if I indeed want to return an array of Double? (For example, consider a case where instead of controlling the type of mult parameter, as in the example above, I instead have no parameters, but instead multiply by a function which I didn't write which returns Double. In that case to multiply the Array elements by that function they need to be converted to Double and a [Double] should be returned. Commented Jan 25, 2017 at 16:44
  • I went ahead and converted my comments to an answer :) Commented Jan 25, 2017 at 18:58
  • @Hamish you might be interested at DoubleConvertible implementation with Initializer as suggested by Martin R here stackoverflow.com/questions/26794282/… which I used here stackoverflow.com/a/29179878/2303865 Commented Jan 25, 2017 at 19:50
  • @LeoDabus Yeah, it's quite a common solution to the problem of "a type which you can pass into a given overload of a function" (in this case Double's init(_:)). You can actually generalise it too in order to work with conversions between different types, see for example my answer here. Commented Jan 25, 2017 at 19:57
  • 1
    @LeoDabus Swift 2 also had protocol composition ;) It just had different syntax (protocol<X, Y, Z>). Commented Jan 25, 2017 at 20:09

1 Answer 1

3

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 }
    }
}
Sign up to request clarification or add additional context in comments.

3 Comments

I would suggest to let it be as an answer :)
Doesn't Swift already have a ConvertibleToDouble protocol or similar?
@Danra Not that I'm aware of (feel free to prove me wrong :) )

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.