The protocol FloatingPointType really accepted by the Double and Float types, but, on the contrary, the protocol for some reason does not include drawings for operator methods, such as (in your case), * binary operator, or += operator. Note the importance of the fact that just because some well-known types accept the protocol, this protocol itself does not have to contain all the drawings that we expect for the types that accept it.
The IntegerType protocol, on the other hand, contains drawings for operator methods.
Therefore, your common T conforming to the FloatingPointType protocol is not necessarily (in the eyes of the fast) multiplicative, etc. since the protocol does not contain any drawings for such operations. If we look at the standard reference library for FloatingPointType , we see that it seems to be used only by Double , Float (and CGFloat ). We know that all three of these types work well, on their own, with our regular operators, so why can't we use these operators for a generic type that matches FloatingPointType ? Again, the set of types corresponding to the protocol does not really make it clear which drawings contain the protocol .
As an example, consider the following protocol: extending some fundamental types to match it
protocol ReallyLotsOfAdditionalStuff {} extension Int : ReallyLotsOfAdditionalStuff {} extension Double : ReallyLotsOfAdditionalStuff {}
The library reference for this dummy protocol will include only those types of Int and Double . Conversely, if we are not careful, we can expect that generics that comply with the ReallyLotsOfAdditionalStuff protocol will at least be added (in addition to a lot of additional materials), but naturally this is not so.
In any case, you can fix this yourself by creating a new protocol, which you add as an additional type constraint for the general T in your FloatingPointType function.
protocol MyNecessaryFloatingPointTypeOperations { func *(lhs: Self, rhs: Self) -> Self func += (inout lhs: Self, rhs: Self)
Therefore, to fix your FloatingPointType , add your own protocol as an additional type constraint for T in the function header:
func linconv<T: protocol<FloatingPointType, MyNecessaryFloatingPointTypeOperations>>(signal_A: [T], signal_B: [T]) -> [T]? { // ... }
Alternatively, let your own protocol inherit from FloatingPointType and add any additional methods your "child protocol" needs, for example:
protocol ImprovedFloatingPointType : FloatingPointType { func *(lhs: Self, rhs: Self) -> Self func += (inout lhs: Self, rhs: Self)
Finally, we can ask if we even need the FloatingPointType protocol (even as the parent protocol for our user protocol)? If we want to make it common for handling two fast floating point types Double and Float , then we could only use the MyNecessaryFloatingPointTypeOperations protocol as a type constraint for our common:
func myFloatingPointGenericFunction<T: MyNecessaryFloatingPointTypeOperations> (a: T, b: T) -> T {
As you already know, we need a common one to comply with the FloatingPointType protocol for one plan: to find out our common function that we can initialize with instances of T using an integer initializer, namely init(_ value: Int) . For example, in your function:
// new array for result var resultSignal = [T](count: N, repeatedValue: T(0)) // <--
However, if this is the only project that we use from the FloatingPointType protocol, we can also add it as a drawing to our own protocol and completely remove the restriction of the general FloatingPointType type.
protocol MyNecessaryFloatingPointTypeOperations { func *(lhs: Self, rhs: Self) -> Self func += (inout lhs: Self, rhs: Self) init(_ value: Int) // ... other necessary floating point blueprints } extension Float: MyNecessaryFloatingPointTypeOperations {} extension Double: MyNecessaryFloatingPointTypeOperations {} func myFloatingPointGenericFunction<T: MyNecessaryFloatingPointTypeOperations> (a: T, b: T) -> T { // ... var c = T(0) // OK c += a * b // OK return c }
At the same time, we understand that for integer types and floating point types, we really do not need two separate generics. Since we (for your example) need only 1. initializer by-int-initializer, binary operator 2. * and operator 3. + = assignments, we could build a common one for all types that correspond to these blue labels as a type constraint, and expand the types that we want to cover with our common protocol.
protocol MyCustomProtocol { func *(lhs: Self, rhs: Self) -> Self func += (inout lhs: Self, rhs: Self) init(_ value: Int) // ... other necessary integer and floating point blueprints } extension Int: MyCustomProtocol {} extension Float: MyCustomProtocol {} extension Double: MyCustomProtocol {} func myIntAndFloatGenericFunction<T: MyCustomProtocol> (a: T, _ b: T) -> T { // ... var c = T(0) // OK c += a * b // OK return c } let aInt = 2 let bInt = 3 let aInt32: Int32 = 2 let bInt32: Int32 = 3 let aDouble = 2.5 let bDouble = 3.0 let cInt = myIntAndFloatGenericFunction(aInt, bInt) // 6 let cInt32 = myIntAndFloatGenericFunction(aInt32, bInt32) // error let cDouble = myIntAndFloatGenericFunction(aDouble, bDouble) // 7.5
Here, however, we see one benefit in using the existing IntegerType protocol: it has already been accepted by numerous integer types, whereas for our user protocol all these int types (if we want to use them in our general form) must be explicitly expanded to accept our custom protocol.
To wrap this up: if you know exactly what types you want to cover with your common, you can populate your own protocol and extend these types to adapt to it. If you want all (numerous) different integer types, using two separate common ones for int and float, with the IntegerType protocol for the latter, you probably prefer.