Search code examples
swiftgenericsprotocolsnumeric

Swift: Multiply two Numerics


protocol DArray: Sequence where Element: Numeric {
    var elements: [Element] { get set }
    subscript(index: Int) -> Element { get set }
    static func *(lhs: Self, rhs: Self) -> Self
}

struct Vector<Element: Numeric>: DArray {
    var elements: [Element]

    init(_ elements: [Element] = []) {
        self.elements = elements
    }

    ...

    static func *<T: DArray>(lhs: Self, rhs: T) -> Self {
        var v = lhs
        var result: Self
        for (i, element) in rhs.enumerated() {
            let e = v[i]
            let r = element * e
            // Cannot convert value of type 'Element' (generic parameter of generic struct 'Vector') to expected argument type 'T.Element' (associated type of protocol 'Sequence')
        }
        return result
    }
}

To the Numeric protocol, the documentation says:

The Numeric protocol provides a suitable basis for arithmetic on scalar values, such as integers and floating-point numbers. You can write generic methods that operate on any numeric type in the standard library by using the Numeric protocol as a generic constraint.

So I chose the Numeric protocol as a generic constraint for the Element type as well as for T.Element. Although both e and element conform to the Numeric protocol I can't multiply them (getting the error message: Cannot convert value of type 'Element' (generic parameter of generic struct 'Vector') to expected argument type 'T.Element' (associated type of protocol 'Sequence')). How do I do that?


Solution

  • Because multiplication is commutative, it doesn't make sense to define the output type of * to be either of the operands' types. Instead, you could allow for all DArrays to be initializable with their elements.

    protocol DArray: Sequence where Element: Numeric {
      var elements: [Element] { get set }
      init<Elements: Sequence>(_: Elements) where Elements.Element == Element
    }
    
    extension Vector {
      init<Elements: Sequence>(_ elements: Elements) where Elements.Element == Element {
        self.init( Array(elements) )
      }
    }
    

    And then, define the operator like this:

    extension DArray {
      static func * <Parameter1: DArray, Output: DArray>(
        dArray0: Self, dArray1: Parameter1
      ) -> Output
      where Parameter1.Element == Element, Output.Element == Element {
        multiply(dArray0, dArray1)
      }
    
      static func * (dArray0: Self, dArray1: Self) -> Self {
        multiply(dArray0, dArray1)
      }
    
      private static func multiply<Parameter0: DArray, Parameter1: DArray, Output: DArray>(
        _ dArray0: Parameter0, _ dArray1: Parameter1
      ) -> Output
      where Parameter0.Element == Parameter1.Element, Parameter1.Element == Output.Element {
        .init( zip(dArray0, dArray1).map(*) )
      }
    }
    

    That way, you can explicitly type the result, as you please, and have an overload for the case where it makes sense to use implicit typing.

    struct 🏹: DArray, IteratorProtocol {
      mutating func next() -> Int? { nil }
    
      var elements: [Element] = []
    
      init<Elements: Sequence>(_ elements: Elements) where Elements.Element == Element { }
    }
    
    
    ( Vector() * 🏹([]) ) as Vector
    ( Vector() * 🏹([]) ) as 🏹
    ( 🏹([]) * Vector() ) as Vector
    ( 🏹([]) * Vector() ) as 🏹
    
    let vector: Vector = ( Vector() * 🏹([]) )
    let 💘: 🏹 = ( 🏹([]) * Vector() )
    
    Vector([1]) * Vector()