Using generics with operators

// I'm trying to figure out how to use Generics work with Operators

// I have 1 goal, to use generics with math operators, but 3 different ways to caompilsh it, depending on the quesion answers

// 1. Ideally there would be a protocol known as computable which let me work with generic types so that I could write

func square<T: ArithmaticComputabale>(a: T) { return a * a }

// this should work for anything that conforms to ArithmaticComputabale,


// But I could find no such protocol so next I looked for

// 2. How to restrict generics to being of certain types. I'd like to be able to write this

func square<T: (Float, Int, Double)>(a: T) { return a * a }

// the purpose being that now I can use square with these types.

// But I couldn't find the syntax for that, so I tried creating my own counting one the identical signatures for multiplycation

// 3. How to accomplish the same thing given that the swift library defines these multiplication variants

infix operator * { associativity left precedence 150 }

public func *(lhs: Float, rhs: Float) -> Float

public func *<T : _IntegerArithmeticType>(lhs: T, rhs: T) -> T

public func *(lhs: Double, rhs: Double) -> Double


// I'd like to be able to do something like this

public protocol Multiplyable {

typealias T

public func *(lhs: T, rhs: T) -> T

}

extension Float: Multiplyable { // should conform already given the swift library

}

extension Double: Multiplyable {

}

extension Int: Multiplyable {

}


// Then finally I'd be able to do this

func square<T: Multiplyable>(a:T) -> T {

return a * a

}


// But everything I've tried there has errored. Does anyone know how to accomplish any or all of these 3?

// Note, not having seen computable, ahd having seen the 3 essentially identical *operator signatures, I'm not sure how these Generics are

// useful for any type where you aren't clear of the conformance rules.

First of all, you should remove the "public" keyword from the *(lhs:rhs:) declaration in your protocol. Protocols are all-or-nothing when it comes to access control, so because you marked the protocol as public, everything is implicitly going to be public as well. The compiler actually enforces this distinction.


Second, I'm not sure it's appropriate to place operator functions in a protocol, based on the kind of errors I get from fiddling with your code in a playground. Feel free to disagree with me on this, though.

The adjective "squared" is a better way to represent your concept. There's cognitive load involved with figuring out if functions are verbs or nouns. You should make squared a computed property of Multiplyable.

i did fiddle with them quiet a bit, including not using public.


what I'm looking for is a way to use generics for arithmatic.


Did you ever get anything to work?


I guess I should clarify my overarching purpose. Squared is not the goal. Learning the design pattern that lets me use operators with generic types is the goal. Squared is just a simplest test case demonstration. The question is answered if the function squared can be written using generic types and having the internal a * a usage.

Thanks for that idea, but squared is just a sample of what I'm trying to accomplish. I am trying to have a generics pattern that I can use for graphics computation functions that allow me to write a single operator overloaded function call and do it in a functional programming style. Squared is the test case that proves the generics pattern works for multiplication. Squared is not the goal. The syntax four using generics with infix operator notation on atrithmatic functions is the goal.

Accepted Answer

Your 3rd trial will work, only if you declare your protocol properly:

public protocol Multiplyable {
    func *(lhs: Self, rhs: Self) -> Self
}
extension Float: Multiplyable {
}
extension Double: Multiplyable {
}
extension Int: Multiplyable {
}
func square<T: Multiplyable>(a:T) -> T {
    return a * a
}
square(1.0 as Float)
square(2.0)
square(3)

Thanks You!


Works like a charm!


Thanks for writing the test case as well. Showed me the simpler float test!


// I'd been doing

let x:Float = 1.0

squared(x) // works, but yourse is cleaner!!


// Looks like Self was the missing secred sauce I needed to getthings going. Thankgs again!

Follow On Questoin:


Is there a systax that will (given your code above ) get this to work?

func averageSumOfSquares<T: Computable>(a:T,b:T) -> T {

return (square(a) + square(b)) / 2 as T /// This is the error line, not sure how to syntax it

}



**** Here's what I have that almost works. It's

public protocol Computable {

func *(lhs: Self, rhs: Self) -> Self

func +(lhs: Self, rhs: Self) -> Self

func /(lhs: Self, rhs: Self) -> Self

}

extension Float: Computable {

}

extension Double: Computable {

}

extension Int: Computable {

}

func square<T: Computable>(a:T) -> T {

return a * a

}

square(1.0 as Float)

square(2.0)

square(3)

func cube<T: Computable>(a: T) -> T {

return a * a * a

}

func sumOfSquares<T: Computable>(a:T,b:T) -> T {

return (square(a) + square(b))

}

sumOfSquares(2.0,b:3.0)


// ****** Everything above worked, thanks to your insight

// But I haven't figured out the syntax of the failing line below


func averageSumOfSquares<T: Computable>(a:T,b:T) -> T {

return (square(a) + square(b)) / 2 as T // ****** But THIS LINE fails

}


//* Is there an improved syntax for that line

// I've tried

return (square(a) + square(b)) / 2 as T

return (square(a) + square(b)) / 2 as Self

return (square(a) + square(b)) / 2 as Comparable

// and

let divider:T = 2.0 as! T

return (square(a) + square(b)) / divider

This is take from my code snippet library:

protocol ArithmeticType: Comparable {
    func +(lhs: Self, rhs: Self) -> Self
    func -(lhs: Self, rhs: Self) -> Self
    func /(lhs: Self, rhs: Self) -> Self
    func *(lhs: Self, rhs: Self) -> Self
    func %(lhs: Self, rhs: Self) -> Self
   
    prefix func ++ (inout val: Self) -> Self
    prefix func -- (inout val: Self) -> Self
    postfix func ++ (inout val: Self) -> Self
    postfix func -- (inout val: Self) -> Self
}
protocol IntegerInitializable: IntegerLiteralConvertible {
    init(_: Int)
    init(_: UInt)
    init(_: Int8)
    init(_: UInt8)
    init(_: Int16)
    init(_: UInt16)
    init(_: Int32)
    init(_: UInt32)
    init(_: Int64)
    init(_: UInt64)
}
protocol IntegerArithmeticType: IntegerInitializable, ArithmeticType {
    func &+(lhs: Self, rhs: Self) -> Self
    func &-(lhs: Self, rhs: Self) -> Self
    func &*(lhs: Self, rhs: Self) -> Self
   
    func << (lhs: Self, rhs: Self) -> Self
    func >> (lhs: Self, rhs: Self) -> Self
}
protocol SignedIntegerArithmeticType: IntegerArithmeticType, SignedNumberType {}
protocol FloatInitializable: FloatLiteralConvertible, IntegerInitializable {
    init(_: Float)
    init(_: Double)
    init(_: CGFloat)
}
protocol FloatArithmeticType: FloatInitializable, ArithmeticType, AbsoluteValuable {}
extension UInt: IntegerArithmeticType {}
extension UInt8: IntegerArithmeticType {}
extension UInt16: IntegerArithmeticType {}
extension UInt32: IntegerArithmeticType {}
extension UInt64: IntegerArithmeticType {}
extension Int: SignedIntegerArithmeticType {}
extension Int8: SignedIntegerArithmeticType {}
extension Int16: SignedIntegerArithmeticType {}
extension Int32: SignedIntegerArithmeticType {}
extension Int64: SignedIntegerArithmeticType {}
extension Float: FloatArithmeticType {}
extension Double: FloatArithmeticType {}
extension CGFloat: FloatArithmeticType {
    init(_ val: CGFloat) {
        self = val
    }
}

With some of above protocols and extension declarations, you can write something like this:

func square<T: ArithmeticType>(a:T) -> T {
    return a * a
}
square(1.0 as Float)
square(2.0)
square(3)

func averageSumOfSquares<T: ArithmeticType where T: IntegerInitializable>(a:T,_ b:T) -> T {
    return (square(a) + square(b)) / 2
}
averageSumOfSquares(1.0 as Float, 4.0 as Float)
averageSumOfSquares(2.0, 5.0)
averageSumOfSquares(3, 6)

Declaring the type as IntegerInitializable, 2 -- IntegerLiteral, can be converted to T. You also need to make all your numeric types you want to use as T, conform to IntegerInitializable.

Perfect! You are awesome. Huge thanks!

Just out of curiosity, OOPer:


I see that you choose to reuse the name IntegerArithmeticType from the stdlib (the Swift.IntegerArithmeticType).


In my (very similar) "numerical-extensions-lib" I decided (without thinking very much) to try to avoid collisions between my names and the ones in the stdlib (My.X and Swift.X), even potentially future names of the stdlib. What are your thoughts about this, ie why/when should we not be "afraid" to reuse/shadow existing std lib names?


Also, do you know if there is an easy way to automatically see what (if any) names are defined in more than one currently imported module (eg My.X and Swift.X)?

What are your thoughts about this, ie why/when should we not be "afraid" to reuse/shadow existing std lib names?

The best possible answer to this question would be, "I had been missing it already exits".

Or more frankly, I do not remember. I would express some concerns about shadowing or something like that if someone else showed me similar thing.


Also, do you know if there is an easy way to automatically see what (if any) names are defined in more than one currently imported module (eg My.X and Swift.X)?

For me, no, and I really appreciate if you know the way and share it with us.


My code has worked just as I expect till now, and will not in the future. So, please do not hesitate to share your work as a better solution.

It is a pity that this unification of arithmetic types and operators isn't in the standard library, since many people appear to be rolling their own versions (including me). For the record, I am using standard library like names, i.e. trying to guess what Apple might use if they added such protocols, in the hope that if they added the protocols it would mean I could delete mine and make minor changes.

Using generics with operators
 
 
Q