Help making Array conform to this simple protocol

Below is my attempt to write a protocol called StringInitializable and make some types conform to it.

Any type T conforming to the protocol can be initialized like this:

let x = T("...")

where "..." is a string representation for a value of type T. It returns an Optional<T> which will be nil if the string is not a valid string representation.

As the code shows, it's easy (and boilerplaty) enough to make eg the basic number types and bool conform, but I'm stuck on how to make Array conform.


So, how to make Array (or better yet, CollectionType) conform to StringInitializable?


Thanks!

(Swift 2, Xcode 7 beta 6)

// Here's the simple protocol:
protocol StringInitializable {
   init?(_ : String)
}

// Adding conformance for these types is easy:
extension Double  : StringInitializable { }
extension Float   : StringInitializable { }
extension Float80 : StringInitializable { }
extension Int     : StringInitializable { init?(_ s : String) { self.init(s, radix: 10) } }
extension UInt    : StringInitializable { init?(_ s : String) { self.init(s, radix: 10) } }
extension Int8    : StringInitializable { init?(_ s : String) { self.init(s, radix: 10) } }
extension UInt8   : StringInitializable { init?(_ s : String) { self.init(s, radix: 10) } }
extension Int16   : StringInitializable { init?(_ s : String) { self.init(s, radix: 10) } }
extension UInt16  : StringInitializable { init?(_ s : String) { self.init(s, radix: 10) } }
extension Int32   : StringInitializable { init?(_ s : String) { self.init(s, radix: 10) } }
extension UInt32  : StringInitializable { init?(_ s : String) { self.init(s, radix: 10) } }
extension Int64   : StringInitializable { init?(_ s : String) { self.init(s, radix: 10) } }
extension UInt64  : StringInitializable { init?(_ s : String) { self.init(s, radix: 10) } }
extension Bool    : StringInitializable {
    init?(_ s : String) {
        switch s {
        case "true" : self.init(true)
        case "false" : self.init(false)
        default: return nil
        }
    }
}


// But here's my failed attempt at Array:
extension Array : StringInitializable {
    init?(_ s : String) {
        let whiteSpaceRemoved = String(s.characters.filter { !" \t\n\r".characters.contains($0) })
        let bracketsRemoved = String(whiteSpaceRemoved.characters.dropFirst().dropLast())
        let arrayOfStrings = bracketsRemoved.characters.split(",").map { String($0) }
        print(arrayOfStrings) // Prints eg ["1", "2", "3"]
        // This won't work:
        // let arrayOfOptionals = arrayOfStrings.map { Element.init($0) }
        return nil // I'll just return nil for now ... : (
    }
}


// Example usage for arrays:
let ok = [Int]("[1, 2, 3]")
print(ok) // Should print [1, 2, 3]

let meh = [Int]("[1, 2.0, 3]")
print(meh) // Should print nil


(The program can be run as is, but Array is obviously not conforming properly to the protocol. It would be nice if only Arrays whose Element conforms to StringInitializable would conform, but it's ok if all Arrays conform and those whose Element does not conform just becomes nil.)

Accepted Answer

This is what I tried first:

extension Array: StringInitializable where Element: StringInitializable {
    //error: extension of type 'Array' with constraints cannot have an inheritance clause
    //...
}

...


Second try:

extension Array : StringInitializable {
    init?(_ s : String) {
        let whiteSpaceRemoved = String(s.characters.filter { !" \t\n\r".characters.contains($0) })
        let bracketsRemoved = String(whiteSpaceRemoved.characters.dropFirst().dropLast())
        let arrayOfStrings = bracketsRemoved.characters.split(",").map { String($0) }
        print(arrayOfStrings) // Prints eg ["1", "2", "3"]
        let arrayOfOptionals: [Element?] = arrayOfStrings.map {
            if let myElementType = Element.self as? StringInitializable.Type {
                return myElementType.init($0) as! Element?
            } else {
                return nil
            }
        }
        if arrayOfOptionals.contains({$0 == nil}) {
            return nil
        }
        self = arrayOfOptionals.map{$0!}
    }
}

Looks like working:

let ok = [Int]("[1, 2, 3]")
print(ok) //->Optional([1, 2, 3])
let meh = [Int]("[1, 2.0, 3]")
print(meh) //->nil

The Element type is fixed for a given invocation - no need to test it more than once:


extension Array : StringInitializable {
    init?(_ s : String) {
        guard let elementType = Element.self as? StringInitializable.Type else { return nil }
        ...
        let arrayOfOptionals = arrayOfStrings.map { elementType.init($0) as! Element? }
        self = arrayOfOptionals.flatMap {$0}
        if self.count != arrayOfOptionals.count {
            return nil
        }
    }
}

Great, thanks!

Help making Array conform to this simple protocol
 
 
Q