Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to convert any generic Numeric into a Double?

Tags:

generics

swift

So I have a method that has 3 different types of arguments that could come in:

Int32, Int and Double. So the idea was to use generics to minimize the interface

func resetProgressBarChunks<T:Numeric>(originalIterationCount: T) {
    guard let iCount = originalIterationCount as? Double else {return}

But what I have realized, is at runtime, the Int32 and Int arguments will actually fail that guard let. It makes sense, it was just wishful thinking on my part.

But if I try to simply cast a Numeric into a double, the compiler will bark:

func resetProgressBarChunks<T:Numeric>(originalIterationCount: T) {
    guard let iCount = Double(originalIterationCount) else {return}

Cannot invoke initializer for type 'Double' with an argument of type '(T)'

Which I suppose also makes sense, because there is no initializer for Double that takes a Generic.

So it looks like I'm about to be forced to write 3 methods with different parameter types. The Int32 and Int parameter types would just cast into a Double and then call the Double method. Is this really the best way? I really was hoping I could leverage Numeric somehow

like image 682
A O Avatar asked Dec 14 '18 00:12

A O


2 Answers

... because there is no initializer for Double that takes a Generic.

That is not entirely true. There is no initializer taking a Numeric argument. But there are generic initializers taking BinaryInteger and BinaryFloatingPoint arguments, so that two overloads are sufficient:

func resetProgressBarChunks<T: BinaryInteger>(originalIterationCount: T) {
    let iCount = Double(originalIterationCount)
    // ...
}

func resetProgressBarChunks<T: BinaryFloatingPoint>(originalIterationCount: T) {
    let iCount = Double(originalIterationCount)
    // ...
}

This covers Double, Int, Int32 arguments as well as Float and all other fixed-size integer types.

like image 186
Martin R Avatar answered Nov 01 '22 17:11

Martin R


Just for purposes of syntactical illustration, here's an example of making this a generic and arriving at a Double for all three types:

func f<T:Numeric>(_ i: T) {
    var d = 0.0
    switch i {
    case let ii as Int:
        d = Double(ii)
    case let ii as Int32:
        d = Double(ii)
    case let ii as Double:
        d = ii
    default:
        fatalError("oops")
    }
    print(d)
}

But whether this is better than overloading is a matter of opinion. In my view, overloading is far better, because with the generic we are letting a bunch of unwanted types in the door. The Numeric contract is a lie. A triple set of overloads for Double, Int, and Int32 would turn the compiler into a source of truth.

like image 32
matt Avatar answered Nov 01 '22 19:11

matt