Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trying to conform to StringLiteralConvertible

I'm running into a few oddities while trying to conform to StringLiteralConvertible:

class Person: StringLiteralConvertible {
    var name = ""

    init(name n:String){
        name = n
    }

    init(stringLiteral value: StringLiteralType){
        name = n
    }

    init(extendedGraphemeClusterLiteral value: ExtendedGraphemeClusterLiteralType){
        name = n
    }

    init(unicodeScalarLiteral value: UnicodeScalarLiteralType){
        name = n
    }
}




var ironMan = Person(name: "Tony Stark")
var spiderMan: Person = "Peter Parker"

I implemented both the ExtendedGraphemeClusterLiteralConvertible and the UnicodeScalarLiteralConvertible protocols (whatever on Earth that means).

However I still got errors and had to provide definitions for both ExtendedGraphemeClusterLiteralType and UnicodeScalarLiteralType:

typealias ExtendedGraphemeClusterLiteralType = String
typealias UnicodeScalarLiteralType = String

Why did I have to provide this, if it's already in the standard header???

The compiler considered it still had the right to complain and forced me to add the required keyword to the inits, even though the definition of the protocols does not include the required keyword! Why????

The following code compiles, but I don't understand why the first version didn't compile!

class Person: StringLiteralConvertible {
    var name = ""

    init(name n:String){
        name = n
    }

    typealias ExtendedGraphemeClusterLiteralType = String
    typealias UnicodeScalarLiteralType = String

    required convenience init(stringLiteral value: StringLiteralType){
        self.init(name: value)
    }

    required convenience init(extendedGraphemeClusterLiteral value: ExtendedGraphemeClusterLiteralType){
        self.init(name: value)
    }

    required convenience init(unicodeScalarLiteral value: UnicodeScalarLiteralType){
        self.init(name: value)
    }
}

Swift, if you like the compiler to yell at you, you're gonna love it! :-P

like image 259
cfischer Avatar asked Nov 08 '14 20:11

cfischer


1 Answers

Why typealiases:

The answer to your first question lies in the fact that Swift protocols that have associated types can be considered abstract, and need to be bound to a specific type. Associated types are an alternative to using a more specialized approach like class MyClass : IsCompatibleWith<Int> {}. I think the reason for this is to have common hierarchies, but this limitation also causes many other headaches. No matter what though, it is reasonable and expected that you need to specify the type to which the protocol is bound. In Swift, you do this by type-aliasing associated type to the one you want (String in your case).

Also note, that the associated type is not specified in the protocol, the protocol only specifies that you must define it. That said, unless you have conflicting methods on your class, the compiler can generally infer the associated type (read this in the docs somewhere).

Required initializers:

It appears that initializers specified in protocols are required by default / nature. The documentation says that you can implement them as designnated or convenience, but that either way they are required:

Class Implementations of Protocol Initializer Requirements

You can implement a protocol initializer requirement on a conforming class as either a designated initializer or a convenience initializer. In both cases, you must mark the initializer implementation with the required modifier:

class SomeClass: SomeProtocol {
required init(someParameter: Int) {
// initializer implementation goes here
}
}

The use of the required modifier ensures that you provide an explicit or inherited implementation of the initializer requirement on all subclasses of the conforming class, such that they also conform to the protocol.

From my reading though, it answers what but not why.

like image 57
Chris Conover Avatar answered Oct 03 '22 21:10

Chris Conover