2

Question background

I have a question based on my own attempt to yield a satisfactory workaround for the behaviour wanted by the OP of the following thread:

The subject is (like a few others here at SO...) attempting to mimic generic array extensions that conform to some protocol. There was a recent similar question regarding this subject in this thread:


Question

My question cover the next step. Lets say we manage to implement a somewhat generic extension to Array, conforming to some protocol blueprints, say MyProtocol. Hence, from now on, assume that Array instances, for some types of Generator.Element (say, for MyTypes), are now extended and conforms to MyProtocol.

Then, for a function taking a generic Array (or, specifically, say <U: _ArrayType ..., or possibly RangeReplaceableCollectionType ... or SequenceType ...) type) as argument, is there any way to add a type constraint to the actual generic array (U), and not just to its Generator.Element type? (so not ... where Generator.Element.Type == SomeTypeConstraint).

Example

I'll try to show my question "in action" to help specify what I'm actually asking. Note that this is the most MWE I've been able to construct for this purpose, my apologies if it's possibly a bit hefty.

Protocols (protocol MyTypes for use as type + protocol MyProtocol used for array extension):

/* Used as type constraint for Generator.Element */
protocol MyTypes {
    var intValue: Int { get }
    init(_ value: Int)
    func *(lhs: Self, rhs: Self) -> Self
    func +=(inout lhs: Self, rhs: Self)
}

extension Int : MyTypes { var intValue: Int { return self } }
extension Double : MyTypes { var intValue: Int { return Int(self) } }
/* Let's not extend 'Float' for now
   extension Float : MyTypes { var intValue: Int { return Int(self) } } */

/* Used as extension to Array : blueprints for extension method
   to Array where Generator.Element: are constrainted to MyTypes */
protocol MyProtocol {
    //typealias T
    func foo<T: MyTypes>(a: [T]) -> Int?
}

Extension of Array by MyProtocol; implementation of blue-printed method:

extension Array : MyProtocol {
    func foo<T: MyTypes>(a: [T]) -> Int? {
        /* [T] is Self? proceed, otherwise return nil */
        if let b = self.first {
            if b is T && self.count == a.count {
                var myMultSum: T = T(0)

                for (i, sElem) in self.enumerate() {
                    myMultSum += (sElem as! T) * a[i]
                }
                return myMultSum.intValue
            }
        }
        return nil
    }
}

Some tests where we, for a function, constrain the Generator.Element of the Array arguments to MyTypes, hence somewhat implicitly asserting that only arrays conforming to MyProtocol can use the function:

/* Tests */
let arr1d : [Double] = [1.0, 2.0, 3.0]
let arr2d : [Double] = [-3.0, -2.0, 1.0]

let arr1f : [Float] = [1.0, 2.0, 3.0]
let arr2f : [Float] = [-3.0, -2.0, 1.0]

func bar1<U: MyTypes> (arr1: [U], _ arr2: [U]) -> Int? {
    return arr1.foo(arr2)
}
let myInt1d = bar1(arr1d, arr2d) // -4, OK

//let myInt1f = bar1(arr1f, arr2f)
    /* Error: Cannot convert value of type '[Float]'
       to expected argument type '[_]'

  OK! Expected as 'Float' does not conform to MyTypes protocol */

We've now arrived to the core of my question, something I haven't been able to construct myself: Is there any possible way to explicitly constrain an array generic, say U, to MyProtocol (for which Array for some Generator.Elements have been constrained)? I would like to do something along the lines

func bar2<T: MyTypes, U: protocol<MyProtocol, _ArrayType> where U.Generator.Element == T> (arr1: U, _ arr2: U) -> Int? {

    // OK, type 'U' behaves as an array type with elements 'T' (==MyTypes)
    var a = arr1
    var b = arr2
    a.append(T(0))
    b.append(T(0))

    return a.foo(b)
        /* Error: Cannot convert value of type 'U'
           to expected argument type '[_]'          */
}
let myInt2 = bar2(arr1d, arr2d)
  • Is this possible, perhaps in a different manner?

(Feel free to edit this question to improve it, I'm not very well versed at the process of asking, and the best practice for, questions here at SO)

1 Answer 1

0

I managed to fix my "do something along the lines" proposal work, by:

  • Switching from <U: SequenceType ... to <U: _ArrayType ... (see edit history),

  • For calling my generic array extension with an generic array (generic U), explicitly initialize an array in the call, explicitly telling reciever that the caller argument (of generic type U) is of type Array (using Array(...) initializer).

Hence, we can add a type constraint (protocol conformance) for a generic array parameter of a function, using the generic array extension as described in the question, followed by the following approach for protocol conformance:

func bar2<T: MyTypes, U: protocol<MyProtocol, _ArrayType> where U.Generator.Element == T> (arr1: U, _ arr2: U) -> Int? {

    // OK, type U behaves as array type with elements T (=MyTypes)
    var a = arr1
    var b = arr2
    a.append(T(2)) // add 2*7 to multsum
    b.append(T(7))

    return a.foo(Array(b))
        /* Ok! */
}
let myInt2 = bar2(arr1d, arr2d) // -4+2*7 = 10, OK
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.