HiveBrain v1.2.0
Get Started
← Back to all entries
patternswiftMinor

Optimal custom operator with generic type constraint for numerical type

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
genericoptimaloperatorwithnumericaltypecustomforconstraint

Problem

I'm implementing (for an article) two custom infix operators:

  • ¿% - to calculate percent of total



  • %? - to calculate the percent that represents a segment of total



After debugging some errors and looking for information I finally found a way to get my code working:

protocol NumericType {

    static func *(lhs: Self, rhs: Self) -> Self
    static func *(lhs: Self, rhs: Int) -> Self
    static func /(lhs: Self, rhs: Self) -> Self
    static func /(lhs: Self, rhs: Int) -> Self

} // NumericType

extension Double : NumericType {

    internal static func *(lhs: Double, rhs: Int) -> Double {
        return lhs * Double(rhs)
    }

    internal static func /(lhs: Double, rhs: Int) -> Double {
        return lhs / Double(rhs)
    }

 } // extension Double : NumericType

extension Float : NumericType {

    internal static func *(lhs: Float, rhs: Int) -> Float {
        return lhs * Float(rhs)
    }

    internal static func /(lhs: Float, rhs: Int) -> Float {
        return lhs / Float(rhs)
    }

 } // extension Float : NumericType

extension Int : NumericType { }

infix operator ¿%

func ¿% (percentage: T, ofThisTotalValue: T) -> T {

    return (percentage * ofThisTotalValue) / 100

} // infix operator ¿%

infix operator %?

func %? (segment: T, ofThisTotalValue: T) -> T {

    return (segment * 100) / ofThisTotalValue

} // infix operator %?

let percentage: Double = 8
let price: Double = 45

let save = percentage ¿% price

print("\(percentage) % of \(price) $ = \(save) $")

print("\(save) $ of \(price) $ = \(save %? price) %")


The output:

8.0 % of 45.0 $ = 3.6 $
3.6 $ of 45.0 $ = 8.0 %


Do you thinks there could be a more optimal and readable approach? Could you give some advice or share an example?

Solution

I imagine that you started out with something like this:

infix operator ¿%

func ¿% (percentage: T, ofTotal: Double) -> Double {
    return percentage * ofTotal / 100.0
}


And received the error:


binary operator '*' cannot be applied to operands of type 'T' and 'Double'

You've solved this by creating a function with signature NumericType * Double -> Double... which works, but as you've found requires a bunch of boilerplate.

The alternative approach is to to find a way to convert NumericType to a double so that in the end you're doing the multiplication on two doubles:

(NumericType -> Double) * Double - > Double

An implementation of this would look something like this:

protocol NumericType {
    var doubleValue: Double { get }
}

infix operator ¿%
infix operator %?

func ¿% (percentage: T, ofTotal: Double) -> Double {
    return percentage.doubleValue * ofTotal / 100.0
}

func %? (segment: T, ofTotal: Double) -> Double {
    return segment.doubleValue * 100 / ofTotal
}

extension Double: NumericType {
    var doubleValue: Double { return self }
}

extension Int: NumericType {
    var doubleValue: Double { return Double(self) }
}


However, please please please reconsider using custom operators like this.

I don't think that it's improving the readability or maintainability of your code, and unless you're using it every single day, I feel like it's something that will cause headaches in the future.

Code Snippets

infix operator ¿%

func ¿% <T: NumericType>(percentage: T, ofTotal: Double) -> Double {
    return percentage * ofTotal / 100.0
}
protocol NumericType {
    var doubleValue: Double { get }
}

infix operator ¿%
infix operator %?

func ¿% <T: NumericType>(percentage: T, ofTotal: Double) -> Double {
    return percentage.doubleValue * ofTotal / 100.0
}

func %? <T: NumericType>(segment: T, ofTotal: Double) -> Double {
    return segment.doubleValue * 100 / ofTotal
}

extension Double: NumericType {
    var doubleValue: Double { return self }
}

extension Int: NumericType {
    var doubleValue: Double { return Double(self) }
}

Context

StackExchange Code Review Q#150024, answer score: 2

Revisions (0)

No revisions yet.