How to solve generic problem: taking any BinaryFloatingPoint convert to NSNumber?

Look to the end to see my problem:

import Foundation

protocol NSNumberConvertible { }
extension NSNumberConvertible {
    var asNSNumber: NSNumber {
        // just force cast b/c we only conform this to known NSNumber convertible Numeric types
        self as! NSNumber
    }
}
extension Decimal: NSNumberConvertible { }
extension Double:  NSNumberConvertible { }
extension Float:   NSNumberConvertible { }
extension Int8:    NSNumberConvertible { }
extension Int32:   NSNumberConvertible { }
extension Int:     NSNumberConvertible { }
extension Int64:   NSNumberConvertible { }
extension UInt8:   NSNumberConvertible { }
extension Int16:   NSNumberConvertible { }
extension UInt:    NSNumberConvertible { }
extension UInt64:  NSNumberConvertible { }
extension UInt16:  NSNumberConvertible { }
extension Bool:    NSNumberConvertible { }

// Prefab one. Is there better way? Can't put this in SpellOutNumberFormatStyle with static computed property
// because with static computed property it's re-computed everytime
let ___$$$spellOutNumberFormatter: NumberFormatter = {
    let formatter = NumberFormatter()
    formatter.numberStyle = .spellOut
    return formatter
}()

struct SpellOutNumberFormatStyle<Value: NSNumberConvertible>: FormatStyle {
    func format(_ value: Value) -> String {
        ___$$$spellOutNumberFormatter.string(from: value.asNSNumber)!
    }
}

extension FormatStyle where Self == SpellOutNumberFormatStyle<Decimal> {
    static var spellOut: Self { .init() }
}
extension FormatStyle where Self == SpellOutNumberFormatStyle<Double> {
    static var spellOut: Self { .init() }
}
extension FormatStyle where Self == SpellOutNumberFormatStyle<Float> {
    static var spellOut: Self { .init() }
}
extension FormatStyle where Self == SpellOutNumberFormatStyle<Int8> {
    static var spellOut: Self { .init() }
}
extension FormatStyle where Self == SpellOutNumberFormatStyle<Int32> {
    static var spellOut: Self { .init() }
}
extension FormatStyle where Self == SpellOutNumberFormatStyle<Int> {
    static var spellOut: Self { .init() }
}
extension FormatStyle where Self == SpellOutNumberFormatStyle<Int64> {
    static var spellOut: Self { .init() }
}
extension FormatStyle where Self == SpellOutNumberFormatStyle<UInt8> {
    static var spellOut: Self { .init() }
}
extension FormatStyle where Self == SpellOutNumberFormatStyle<Int16> {
    static var spellOut: Self { .init() }
}
extension FormatStyle where Self == SpellOutNumberFormatStyle<UInt> {
    static var spellOut: Self { .init() }
}
extension FormatStyle where Self == SpellOutNumberFormatStyle<UInt64> {
    static var spellOut: Self { .init() }
}
extension FormatStyle where Self == SpellOutNumberFormatStyle<UInt16> {
    static var spellOut: Self { .init() }
}
extension FormatStyle where Self == SpellOutNumberFormatStyle<Bool> {
    static var spellOut: Self { .init() }
}


let n = 3.1415926
print(n.formatted(.spellOut))   // three point one four one five nine two six

// ??? How to make it work for this generic:
func foo<T: BinaryFloatingPoint>(_ value: T) {
    value.formatted(.spellOut)      // error: error: ambiguous use of 'spellOut'
}

Edit: I solved it:

func foo<T: BinaryFloatingPoint>(_ value: T) -> String {
    Double(value).formatted(.spellOut)      // error: error: ambiguous use of 'spellOut'
}
1 Like