Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift number generics?

I have this function that is going to calculate the hypotenuse from 2 numbers

func hypotenusa<T>(nr1: T, nr2: T) -> T {
    return sqrt( pow(nr1, 2) + pow(nr2, 2) )
}

// v Simpler situation v

func addition<T>(nr1: T, nr2: T) -> T {
    return nr1 + nr2
}

I want to use generics so I don't have to make 3 copies of this which uses Int, Float, Double separately

But this isn't working, I think generics is really difficult to work with, please help me :)

like image 792
Arbitur Avatar asked Feb 11 '15 14:02

Arbitur


2 Answers

Swift generics aren't like C++ templates.

In C++, you can just try to use a parameterized type however you want, and it's not an error until the compiler tries to instantiate the template with some type that doesn't support what your template tries to do.

In Swift, the generic construct can only use a parameterized type in ways known to be valid when the generic construct is first parsed. You specify these "ways known to be valid" by constraining the parameterized type with protocols.

You cannot call sqrt or pow with generic-typed arguments, because those functions are not themselves generic. They have each two definitions:

func pow(_: Double, _: Double) -> Double
func pow(lhs: Float, rhs: Float) -> Float
func sqrt(x: Double) -> Double
func sqrt(x: Float) -> Float

You could write type-specific versions of hypotenusa:

func hypotenusa(a: Float, b: Float) -> Float
func hypotenusa(a: Double, b: Double) -> Double
func hypotenusa(a: CGFloat, b: CGFloat) -> CGFloat

I'm not sure why you'd create an Int version at all, since very few right triangles have integer hypotenuses.

Anyway, you don't need to define the Float and Double versions at all, because the standard library already provides a hypot function defined on Float and Double:

func hypot(_: Double, _: Double) -> Double
func hypot(lhs: Float, rhs: Float) -> Float

You could create another override for CGFloat:

func hypot(l: CGFloat, r: CGFloat) -> CGFloat {
    return hypot(Double(l), Double(r))
}

As for your addition function, it has the same problem as your hypotenusa function: the + operator is not defined entirely generically. It has some generic definitions (unlike sqrt and pow), but those only cover the integer types (see IntegerArithmeticType). There's not generic definition of + that covers the floating-point types. Swift defines all of these versions of + with explicit types:

func +(lhs: Float, rhs: Float) -> Float
func +<T>(lhs: Int, rhs: UnsafePointer<T>) -> UnsafePointer<T>
func +<T>(lhs: UnsafePointer<T>, rhs: Int) -> UnsafePointer<T>
func +(lhs: Int, rhs: Int) -> Int
func +(lhs: UInt, rhs: UInt) -> UInt
func +(lhs: Int64, rhs: Int64) -> Int64
func +(lhs: UInt64, rhs: UInt64) -> UInt64
func +<T>(lhs: Int, rhs: UnsafeMutablePointer<T>) -> UnsafeMutablePointer<T>
func +<T>(lhs: UnsafeMutablePointer<T>, rhs: Int) -> UnsafeMutablePointer<T>
func +(lhs: Int32, rhs: Int32) -> Int32
func +(lhs: UInt32, rhs: UInt32) -> UInt32
func +(lhs: Int16, rhs: Int16) -> Int16
func +(lhs: UInt16, rhs: UInt16) -> UInt16
func +(lhs: Int8, rhs: Int8) -> Int8
func +(lhs: UInt8, rhs: UInt8) -> UInt8
func +(lhs: Double, rhs: Double) -> Double
func +(lhs: String, rhs: String) -> String
func +(lhs: Float80, rhs: Float80) -> Float80
like image 113
rob mayoff Avatar answered Oct 03 '22 09:10

rob mayoff


With Swift 5, according to your needs, you can pick one of the following ways in order to solve your problem.


#1. Using FloatingPoint protocol as a parameter generic constraint

The Apple Developer Documentation for FloatingPoint shows the following hypotenuse function implementation as an example of FloatingPoint usage:

func hypotenuse<T: FloatingPoint>(_ a: T, _ b: T) -> T {
    return (a * a + b * b).squareRoot()
}

let (dx, dy) = (3.0, 4.0)
let result = hypotenuse(dx, dy)
print(result) // prints: 5.0

#2. Using AdditiveArithmetic protocol as a parameter generic constraint

AdditiveArithmetic has the following declaration:

A type with values that support addition and subtraction.

The Playground sample code below shows a possible usage of AdditiveArithmetic as a function parameter generic constraint:

func addition<T: AdditiveArithmetic>(a: T, b: T) -> T {
    return a + b
}

let result = addition(a: 3, b: 4)
print(result) // prints: 7

#3. Using Numeric protocol as a parameter generic constraint

Numeric has the following declaration:

A type with values that support multiplication.

The Playground sample code below shows a possible usage of Numeric as a function parameter generic constraint:

func multiply<T: Numeric>(a: T, b: T, c: T) -> T {
    return a * b * c
}

let result = multiply(a: 3, b: 4, c: 5)
print(result) // prints: 60

Note that Numeric protocol inherit from AdditiveArithmetic protocol.


The Apple Developer Documentation contains a dedicated page for all numeric protocols: Numeric Protocols.

like image 34
Imanou Petit Avatar answered Oct 03 '22 08:10

Imanou Petit