How to use FloatingPoint generic type for Float/Double - swift

I'd like to make the function below work both with Float and Double values:
func srgb2linear(_ S: Float) -> Float {
if S <= 0.04045 {
return S / 12.92
} else {
return pow((S + 0.055) / 1.055, 2.4)
}
}
The Swift 4 Documentation says that what I need is a FloatingPoint generic, to represent both Float and Double classes, like:
func srgb2linear<T: FloatingPoint>(_ S: T) -> T
However when I try to do this, it doesn't compile with the following errors:
Error: binary operator '<=' cannot be applied to operands of type 'T' and 'Double'
Error: binary operator '/' cannot be applied to operands of type 'T' and 'Double'
Error: binary operator '+' cannot be applied to operands of type 'T' and 'Double'
How is it possible that for a generic representing floating point numbers such operators are not implemented? And if not like this, how can I write this function in Swift?

One problem is that FloatingPoint is not a subprotocol of ExpressibleByFloatLiteral, so your floating-point literals cannot necessarily be converted to T. You can solve this either by changing FloatingPoint to BinaryFloatingPoint (which is a subprotocol of ExpressibleByFloatLiteral) or by adding ExpressibleByFloatLiteral as a separate requirement.
Then you will run into the problem that there is no pow function that is generic over FloatingPoint, and no member of FloatingPoint or BinaryFloatingPoint that performs exponentiation. You can solve this by creating a new protocol and conforming the existing floating-point types to it:
protocol Exponentiatable {
func toPower(_ power: Self) -> Self
}
extension Float: Exponentiatable {
func toPower(_ power: Float) -> Float { return pow(self, power) }
}
extension Double: Exponentiatable {
func toPower(_ power: Double) -> Double { return pow(self, power) }
}
extension CGFloat: Exponentiatable {
func toPower(_ power: CGFloat) -> CGFloat { return pow(self, power) }
}
Note that there is also a Float80 type, but the standard library doesn't provide a pow function for it.
Now we can write a working generic function:
func srgb2linear<T: FloatingPoint>(_ S: T) -> T
where T: ExpressibleByFloatLiteral, T: Exponentiatable
{
if S <= 0.04045 {
return S / 12.92
} else {
return ((S + 0.055) / 1.055).toPower(2.4)
}
}

You could define a second one with Double arguments:
func srgb2linear(_ S: Double) -> Double {
if S <= 0.04045 {
return S / 12.92
} else {
return pow((S + 0.055) / 1.055, 2.4)
}
}
or, if Float<->Double conversions are not a concern :
func srgb2linear(_ S: Double) -> Double {
return Double(srgb2linear(Float(S)))
}

Related

How do you write a protocol specifying the existence of an arithmetic operator in Swift?

Here's an example. Writing a function to multiply two doubles is pretty simple:
func MultiplyDoubles(_ x: Double, _ y: Double) {
return x * y
}
MultiplyDoubles(3,5) // returns 15
But suppose I wanted to write a generic function to do this:
func MultiplyValues<T>(_ x: T, _ y: T) {
return x * y //ERROR
}
MultiplyValues(3, 5)
But this throws an error: Binary operator '*' cannot be applied to to 'T' operands.
I understand that I'd need to write a protocol specifying that * can be applied to it, but how would I do this?
I tried doing:
protocol Multipliable {
static func *(A:Multipliable, B: Multipliable) -> Multipliable
}
func MultiplyValues<T: Multipliable>(_ x: T, _ y: T) -> Multipliable {
return x * y
}
MultiplyValues(3, 5)
Although this returns the message
error: MyPlayground.playground:15:19: error: argument type 'Int' does not conform to expected type 'Multipliable'
MultiplyValues(3, 5)
^
Research:
I've already looked at the documentation for protocols as well as the documentation for generics and for operators. I'm not writing a generic protocol, but rather a normal protocol that's meant to specify that it can be multiplied. This documentation explained how to overload operators, as well as how to create generic functions, and use protocols with generic functions, but none of them explained how to write a protocol specifying that an operator could be applied to the protocol.
You can use the existing Numeric protocol:
func multiply<T: Numeric>(_ x: T, _ y: T) -> T {
return x * y
}
But, let's imagine that such a protocol didn't exist. You can define your own and the specify which types conform to this protocol:
protocol Multipliable {
static func *(lhs: Self, rhs: Self) -> Self
}
// These types already implement the above method, so all you need to do
// is declare conformance to your protocol with an empty extension.
extension Int: Multipliable { }
extension Double: Multipliable { }
// repeat for other types as you see fit
And then you can do:
func multiply<T: Multipliable>(_ x: T, _ y: T) -> T {
return x * y
}
You can write something like this.
protocol Multipliable {
static func *(A:Self, B: Self) -> Self
}
extension Int : Multipliable {}
//You need other extensions for other numeric types...
func MultiplyValues<T: Multipliable>(_ x: T, _ y: T) -> T {
return x * y
}
MultiplyValues(3, 5)
But current Swift has some numeric protocols and you should better utilize them (as in Rob's answer).

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

Contextual type inference for Type in Swift 2.2+

I want to use first order citizen Type in Swift to decide which function to call.
func sf(v: [Float]) -> Float{
}
func df(v: [Double]) -> Double {
}
func f<T: RealType>(seq ls: [T]) -> T {
if T.self == Float.self {
return sf(ls) // 1. where sf: Float -> Void
} else if T.self == Double.self {
return df(ls) // 2. where df : Double -> Void
}
}
The type inference system couldn't notice that under one branch T == Float and Double in the other ?
Is here a missing feature, complex feature or bug ?
Edit:
typealias RealType = protocol<FloatingPointType, Hashable, Equatable, Comparable, FloatLiteralConvertible, SignedNumberType>
for my prototype but will become a protocol
You are trying to combine static resolution given by generic with runtime decisions, and this is not possible.
You can simply overload f for both Float and Double to obtain what you need:
func f(seq ls: [Float]) -> Float {
return sf(ls) // 1. where sf: Float -> Void
}
func f(seq ls: [Double]) -> Double {
return df(ls) // 2. where df : Double -> Void
}
However, if you want RealType to be a generic placeholder that you can use over other types than Float or, Double, then you can do something like this:
protocol RealType {
static func processArray(v: [Self]) -> Self
}
extension Float: RealType {
static func processArray(v: [Float]) -> Float {
return sf(v)
}
}
extension Double: RealType {
static func processArray(v: [Double]) -> Double {
return df(v)
}
}
func sf(v: [Float]) -> Float{
return 0
}
func df(v: [Double]) -> Double {
return 0
}
func f<T: RealType>(seq ls: [T]) -> T {
return T.processArray(ls)
}
This will give you both type safety, which is one of Swift's main advantages, and scalability as whenever you need to add support for f over another type, you need to only declare that type as conforming to RealType, and implement the processArray method.

Function with generic type

I have a function that can calculate the sum of numbers in array with condition like so:
func sumOfArrayWithCondition(array: [Int], filter: (element: Int) -> Bool) -> Int {
var result = 0
for i in 0..<array.count where filter(element: array[i]) {
result += array[i]
}
return result
}
Now I want it to work with Int, Float, Double type. I have tried, but didn't work.
protocol Addable {
func +(lhs: Self, rhs: Self) -> Self
}
extension Int: Addable {}
extension Double: Addable {}
extension Float: Addable {}
func sumOfArrayWithCondition<T: Addable>(array: [T], filter: (element: T) -> Bool) -> T {
var result = 0
for i in 0..<array.count where filter(element: array[i]) {
result += array[i] // <-------- Error here
}
return result // <-------- Error here
}
But it says:
Binary operator '+=' cannot be applied to operands of type 'Int' and 'T'
So how to do it.
Any helps would be appreciated. Thanks.
First issue is that the compiler is inferring the type Int for the var result because you don't declare a type and initialize it with 0. But you need result to be of type T.
First, in order to initialize result as an instance of type T with the value 0, you need to specify that Addable is also IntegerLiteralConvertible, which is already true for Int, Double and Float. Then you can declare result as type T and go from there.
As Rob pointed out, you also need to add the += function to your protocol if you want to be able to use it.
So the final code that achieves what you are looking for is:
protocol Addable : IntegerLiteralConvertible {
func +(lhs: Self, rhs: Self) -> Self
func +=(inout lhs: Self, rhs: Self)
}
extension Int: Addable {}
extension Double: Addable {}
extension Float: Addable {}
func sumOfArrayWithCondition<T: Addable>(array: [T], filter: (element: T) -> Bool) -> T {
var result:T = 0
for i in 0..<array.count where filter(element: array[i]) {
result += array[i]
}
return result
}
As Rob said, result is an Int. But there's no need to create that method at all. You're wanting to call that like so, based on your method signature:
let sum = sumOfArrayWithCondition(myArray, filter: myFilter)
instead, all you have to do is use existing methods provided by swift:
let sum = myArray.filter(myFilter).reduce(0) { $0 + $1 }

How can we create a generic Array Extension that sums Number types in Swift?

Swift lets you create an Array extension that sums Integer's with:
extension Array {
func sum() -> Int {
return self.map { $0 as Int }.reduce(0) { $0 + $1 }
}
}
Which can now be used to sum Int[] like:
[1,2,3].sum() //6
But how can we make a generic version that supports summing other Number types like Double[] as well?
[1.1,2.1,3.1].sum() //fails
This question is NOT how to sum numbers, but how to create a generic Array Extension to do it.
Getting Closer
This is the closest I've been able to get if it helps anyone get closer to the solution:
You can create a protocol that can fulfills what we need to do, i.e:
protocol Addable {
func +(lhs: Self, rhs: Self) -> Self
init()
}
Then extend each of the types we want to support that conforms to the above protocol:
extension Int : Addable {
}
extension Double : Addable {
}
And then add an extension with that constraint:
extension Array {
func sum<T : Addable>(min:T) -> T
{
return self.map { $0 as T }.reduce(min) { $0 + $1 }
}
}
Which can now be used against numbers that we've extended to support the protocol, i.e:
[1,2,3].sum(0) //6
[1.1,2.1,3.1].sum(0.0) //6.3
Unfortunately I haven't been able to get it working without having to supply an argument, i.e:
func sum<T : Addable>(x:T...) -> T?
{
return self.map { $0 as T }.reduce(T()) { $0 + $1 }
}
The modified method still works with 1 argument:
[1,2,3].sum(0) //6
But is unable to resolve the method when calling it with no arguments, i.e:
[1,2,3].sum() //Could not find member 'sum'
Adding Integer to the method signature also doesn't help method resolution:
func sum<T where T : Integer, T: Addable>() -> T?
{
return self.map { $0 as T }.reduce(T()) { $0 + $1 }
}
But hopefully this will help others come closer to the solution.
Some Progress
From #GabrielePetronella answer, it looks like we can call the above method if we explicitly specify the type on the call-site like:
let i:Int = [1,2,3].sum()
let d:Double = [1.1,2.2,3.3].sum()
As of Swift 2 it's possible to do this using protocol extensions. (See The Swift Programming Language: Protocols for more information).
First of all, the Addable protocol:
protocol Addable: IntegerLiteralConvertible {
func + (lhs: Self, rhs: Self) -> Self
}
extension Int : Addable {}
extension Double: Addable {}
// ...
Next, extend SequenceType to add sequences of Addable elements:
extension SequenceType where Generator.Element: Addable {
var sum: Generator.Element {
return reduce(0, combine: +)
}
}
Usage:
let ints = [0, 1, 2, 3]
print(ints.sum) // Prints: "6"
let doubles = [0.0, 1.0, 2.0, 3.0]
print(doubles.sum) // Prints: "6.0"
I think I found a reasonable way of doing it, borrowing some ideas from scalaz and starting from your proposed implementation.
Basically what we want is to have typeclasses that represents monoids.
In other words, we need:
an associative function
an identity value (i.e. a zero)
Here's a proposed solution, which works around the swift type system limitations
First of all, our friendly Addable typeclass
protocol Addable {
class func add(lhs: Self, _ rhs: Self) -> Self
class func zero() -> Self
}
Now let's make Int implement it.
extension Int: Addable {
static func add(lhs: Int, _ rhs: Int) -> Int {
return lhs + rhs
}
static func zero() -> Int {
return 0
}
}
So far so good. Now we have all the pieces we need to build a generic `sum function:
extension Array {
func sum<T : Addable>() -> T {
return self.map { $0 as T }.reduce(T.zero()) { T.add($0, $1) }
}
}
Let's test it
let result: Int = [1,2,3].sum() // 6, yay!
Due to limitations of the type system, you need to explicitly cast the result type, since the compiler is not able to figure by itself that Addable resolves to Int.
So you cannot just do:
let result = [1,2,3].sum()
I think it's a bearable drawback of this approach.
Of course, this is completely generic and it can be used on any class, for any kind of monoid.
The reason why I'm not using the default + operator, but I'm instead defining an add function, is that this allows any type to implement the Addable typeclass. If you use +, then a type which has no + operator defined, then you need to implement such operator in the global scope, which I kind of dislike.
Anyway, here's how it would work if you need for instance to make both Int and String 'multipliable', given that * is defined for Int but not for `String.
protocol Multipliable {
func *(lhs: Self, rhs: Self) -> Self
class func m_zero() -> Self
}
func *(lhs: String, rhs: String) -> String {
return rhs + lhs
}
extension String: Multipliable {
static func m_zero() -> String {
return ""
}
}
extension Int: Multipliable {
static func m_zero() -> Int {
return 1
}
}
extension Array {
func mult<T: Multipliable>() -> T {
return self.map { $0 as T }.reduce(T.m_zero()) { $0 * $1 }
}
}
let y: String = ["hello", " ", "world"].mult()
Now array of String can use the method mult to perform a reverse concatenation (just a silly example), and the implementation uses the * operator, newly defined for String, whereas Int keeps using its usual * operator and we only need to define a zero for the monoid.
For code cleanness, I much prefer having the whole typeclass implementation to live in the extension scope, but I guess it's a matter of taste.
In Swift 2, you can solve it like this:
Define the monoid for addition as protocol
protocol Addable {
init()
func +(lhs: Self, rhs: Self) -> Self
static var zero: Self { get }
}
extension Addable {
static var zero: Self { return Self() }
}
In addition to other solutions, this explicitly defines the zero element using the standard initializer.
Then declare Int and Double as Addable:
extension Int: Addable {}
extension Double: Addable {}
Now you can define a sum() method for all Arrays storing Addable elements:
extension Array where Element: Addable {
func sum() -> Element {
return self.reduce(Element.zero, combine: +)
}
}
Here's a silly implementation:
extension Array {
func sum(arr:Array<Int>) -> Int {
return arr.reduce(0, {(e1:Int, e2:Int) -> Int in return e1 + e2})
}
func sum(arr:Array<Double>) -> Double {
return arr.reduce(0, {(e1:Double, e2:Double) -> Double in return e1 + e2})
}
}
It's silly because you have to say arr.sum(arr). In other words, it isn't encapsulated; it's a "free" function sum that just happens to be hiding inside Array. Thus I failed to solve the problem you're really trying to solve.
3> [1,2,3].reduce(0, +)
$R2: Int = 6
4> [1.1,2.1,3.1].reduce(0, +)
$R3: Double = 6.3000000000000007
Map, Filter, Reduce and more
From my understanding of the swift grammar, a type identifier cannot be used with generic parameters, only a generic argument. Hence, the extension declaration can only be used with a concrete type.
It's doable based on prior answers in Swift 1.x with minimal effort:
import Foundation
protocol Addable {
func +(lhs: Self, rhs: Self) -> Self
init(_: Int)
init()
}
extension Int : Addable {}
extension Int8 : Addable {}
extension Int16 : Addable {}
extension Int32 : Addable {}
extension Int64 : Addable {}
extension UInt : Addable {}
extension UInt8 : Addable {}
extension UInt16 : Addable {}
extension UInt32 : Addable {}
extension UInt64 : Addable {}
extension Double : Addable {}
extension Float : Addable {}
extension Float80 : Addable {}
// NSNumber is a messy, fat class for ObjC to box non-NSObject values
// Bit is weird
extension Array {
func sum<T : Addable>(min: T = T(0)) -> T {
return map { $0 as! T }.reduce(min) { $0 + $1 }
}
}
And here: https://gist.github.com/46c1d4d1e9425f730b08
Swift 2, as used elsewhere, plans major improvements, including exception handling, promises and better generic metaprogramming.
Help for anyone else struggling to apply the extension to all Numeric values without it looking messy:
extension Numeric where Self: Comparable {
/// Limits a numerical value.
///
/// - Parameter range: The range the value is limited to be in.
/// - Returns: The numerical value clipped to the range.
func limit(to range: ClosedRange<Self>) -> Self {
if self < range.lowerBound {
return range.lowerBound
} else if self > range.upperBound {
return range.upperBound
} else {
return self
}
}
}