I run a bunch of calculations using Decimal and Double. Without generics there'd be a lot of repetitive code. There are places I eventually need the Decimal in a more native format but I can't seem to figure out how to do that in Swift.
A hypothetical example: there is no log2() in Decimal, but because of acceptable margin of error I want to convert it to Double and use it that way.
Originally I thought I'd just create a generic function that would work for both Double and Decimal
// Dtype is either Decimal or Double
typealias Dtype = SignedNumeric
func mylog2<T: Dtype>(_ x: T) -> Double
{
let x_double: Double = Double(truncating: x as NSNumber) //'T' is not convertible to 'NSNumber'
return log2(x_double)
}
But I get an error, so then I thought I'd just separate them out into discrete functions. After thinking about it I figure T can't expose its actual Type. Then I thought I'd split the function into their discrete Types and try to use it that way:
func mylog2(_ x: Decimal) -> Double
{
let x_double: Double = Double(truncating: x as NSNumber)
return log2(x_double)
}
func mylog2(_ x: Double) -> Double
{
return log2(x)
}
But then I don't know how to get T to use the mylog2() function by discrete types:
func myExample<T: Dtype>(_ a: T, _ b: T) -> Double
{
let c: T = (a + b) * (b - a)
//bunch of other maths funcs
return mylog2(c) //No exact matches in call to global function 'mylog2'
}
I am pretty sure in other languages there are ways to handle situations like this so there must be a way. I may have approached the problem with an incorrect Swift mindset and there's a completely different way I'm not aware of how to do this. Anyway if someone can help me with the Swift way of tackling this I'd appreciate it.
Just declare your own protocol that requires a log2
method to be implemented. Conform both Double
and Decimal
to this protocol.
protocol Log2 {
func log2() -> Double
}
extension Decimal: Log2 {
func log2() -> Double {
Darwin.log2(Double(truncating: self as NSNumber))
}
}
extension Double: Log2 {
func log2() -> Double {
Darwin.log2(self)
}
}
Then you can constrain the generic type parameter to be Log2 & SignedNumeric
.
typealias Dtype = SignedNumeric & Log2
func myExample<T: Dtype>(_ a: T, _ b: T) -> Double
{
let c: T = (a + b) * (b - a)
//bunch of other maths funcs
return c.log2()
}
Alternatively, make Dtype
its own protocol, replacing Log2
.
protocol Dtype: SignedNumeric {
func log2() -> Double
// other functions you need on both Double and Decimal goes here...
}