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.
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 }
}
}