swiftgenericscustom-operator

How To Only Accept Numbers For An Operator Using Generics In Swift?


I'm trying to create an operator for numbers. For example an operator that increments a number by 10.

This is the code I wrote:

prefix operator +++{}

prefix operator +++<T>(inout operand: T) -> T{
    operand += 10
    return operand
}

There is an error with my += operator. it requires numeric operands. so I did this:

protocol Numeric {}

extension Int: Numeric {}
extension Float: Numeric {}
extension Double: Numeric {}

prefix operator +++ {}

prefix operator +++<T: Numeric>(inout operand: T) -> T {
    operand += 10
    return operand
}

But it failed to compile. Anybody have any ideas?


Solution

  • The problem is that your Numeric protocol does not guarantee a += operator will be present.

    Consider this:

    // Numeric imposes no requirements, so this will compile
    extension Range: Numeric { }
    // but Range has no += operator, so +++ could not work
    

    Instead, you would have to add += as a requirement of Numeric:

    protocol Numeric: IntegerLiteralConvertible {
        func +=(inout lhs: Self,rhs: Self)
    }
    

    Note, you also need Numeric to conform to IntegerLiteralConvertible so that you can create a 10 of the appropriate type to add to it.

    Now, this compiles and runs fine, because Numeric guarantees all the features it uses will be available:

    prefix operator +++{}
    
    prefix func +++<T: Numeric>(inout operand: T) -> T {
        operand += 10
        return operand
    }
    
    var i = 10
    +++i  // i is now 20
    

    That said, there is already a protocol that does what you need: Strideable, to which all the standard numeric types conform.

    protocol Strideable {
    // (actually _Strideable but don’t worry about that)
    
        /// A type that can represent the distance between two values of `Self`.
        typealias Stride : SignedNumberType
        // note, SignedNumberType conforms to IntegerLiteralConvertible
    
        /// Returns a `Self` `x` such that `self.distanceTo(x)` approximates
        /// `n`.
        ///
        /// - Complexity: O(1).
        ///
        /// - SeeAlso: `RandomAccessIndexType`'s `advancedBy`, which
        ///   provides a stronger semantic guarantee.
        func advancedBy(n: Self.Stride) -> Self
    }
    

    And an implementation of += that uses it:

    func +=<T : Strideable>(inout lhs: T, rhs: T.Stride)
    

    This means you can implement +++ like this:

    prefix func +++<T: Strideable>(inout operand: T) -> T { 
        operand = operand.advancedBy(10)
        return operand 
    }