Satisfying Expressiblebyarrayliteral Protocol

Satisfying ExpressibleByArrayLiteral Protocol

The reason is inheritance: any init inside a protocol must be marked as required if adopted by a class. A struct cannot be inherited.

For a class, An init must initialize an instance of that same class or return nil. When that class adopts a protocol, itself and all its subclasses must provide their own implementations so the compiler can verify that fact:

class ClassA : ExpressibleByArrayLiteral {
required init(arrayLiteral elements: Self.Element...) {
// this implicitly returns an instance of ClassA
}
}

class ClassB : ClassA {
// without its own init(arrayLitteral:), it will fallback on ClassA's init and return
// an instance of ClassA, which Swift does not allow.
}

This is due to Swift's static typing system. Unlike in Objective-C where you think you create an NSString but actually get an _NSContiguousString instead (the so-called class cluster design pattern)

NSString * str = [[NSString alloc] initWithString:@"Hello world"];
// surprise: you may get an _NSContiguousString instead

Just get rid of the extension and implement it in the class's definition:

public class Stack<T> : ExpressibleByArrayLiteral {
private var buffer: [T]

init() {
self.buffer = []
}

public func push(_ value: T) {
self.buffer.append(value)
}

public func pop() -> T? {
return self.buffer.popLast()
}

var size: Int {
return self.buffer.count
}

var isEmpty: Bool {
return self.size == 0
}

// MARK: -
required public init(arrayLiteral: T...) {
self.buffer = []
for item in arrayLiteral {
self.push(item)
}
}
}

`ExpressibleByArrayLiteral` conformed to by class and its superclass = superclass is not convertible to subclass

I've ended up with a solution using a postfix operator.

Since I need to be able to instatiate the UIKits UILabel using ExpressibleByArrayLiteral I cannot use murphguys proposed solution with Label and SecondLabel.

My original code works by adding this postfix operator:

postfix operator ^
postfix func ^<I: Instantiatable>(attributes: [I.Element]) -> I {
return I(values: attributes)
}

Which makes the code compile and work. Although it feels a bit "hacky"...

let custom: MyLabel = [1, 2, 3, 4]^ // note use of caret operator. Now compiles
print(custom.text!) // prints "Sum 10"

If you are interested in why and how I use this you can take a look at my framework ViewComposer which enables this syntax:

let label: UILabel = [.text("Hello World"), .textColor(.red)]

But I also wanted to be able to create my own Composable subclass called MyLabel (or just Label..)

let myLabel: MyLabel = [.text("Hello World"), .textColor(.red)] // does not compile

Which did not work earlier, but now works using the caret postfix operator ^, like this:

let myLabel: MyLabel = [.text("Hello World"), .textColor(.red)]^ // works!

Which for now is the most elegant solution I think.

How to satisfy a protocol which includes an initializer?

Ok, my bad.

To guarantee that all subclasses conform to MyProtocol new initializer has to be marked as required as well.

Furthermore Swift requires to declare all required initializers directly within the class and does not allow to declare them in extensions.

extension MyClass : MyProtocol {
required convenience init(name: String) {
self.init()
self.name = name
}
}

How a generic class to conform a protocol (that have init in it) with constraint?

I don't know of a way to achieve this without making Graph final. If you do make it final, however, it will work fine as long as you remove required (required doesn't mean anything if there can't be subclasses):

extension Graph: ExpressibleByArrayLiteral where D == Int {
convenience init(arrayLiteral elements: Node...) {
self.init(Array(zip(0..., elements)))
}
}

Swift: Implementing Protocol Initializer in a Class

Surely the designated superclass initializer would be inherited?

No, not always. If the subclass defines its own designated initialisers, then it won't automatically inherit the superclass' designated initialisers. Consider the following example:

class Foo {
init() {}
}

class Bar : Foo {

var str: String

init(str: String) {
self.str = str
}
}

let b = Bar() // illegal – what value would the 'str' property have?

As Bar defines its own init(str:) designated initialiser, it doesn't automatically inherit Foo's designated initialiser init(). This prevents unsafe initialisation in cases where the subclass declares its own stored properties.

Marking init() as required enforces Bar has an init(), be it through providing its own implementation:

class Foo {
required init() {}
}

class Bar : Foo {

var str: String

init(str: String) {
self.str = str
}

// implement required init(), as Bar defines its own designated initialiser.
required init() {
self.str = "foo" // now str is correctly initialised when calling init()
}
}

let b = Bar() // now legal

Or by inheriting Foo's implementation (when Bar doesn't define its own designated initialisers):

class Foo {
required init() {}
}

class Bar : Foo {
// inherits init() from Foo, as Bar doesn't define its own designed initialisers.
}

let b = Bar() // legal

ArrayLiteralConvertible: Just a normal protocol?

Generally, literals are a purely compile-time artifact. They can be used to produce an object initialized from that literal, but once the compilation phase is over, nobody knows that something was a literal.

This suggests that any support for the protocols below needs to be built into the compiler itself:

  • ArrayLiteralConvertible
  • BooleanLiteralConvertible
  • DictionaryLiteralConvertible
  • ExtendedGraphemeClusterLiteralConvertible
  • FloatLiteralConvertible
  • NilLiteralConvertible
  • IntegerLiteralConvertible
  • StringLiteralConvertible
  • StringInterpolationConvertible
  • UnicodeScalarLiteralConvertible

The compiler would not take your own protocol as a replacement of any of the above.

Create a copyable protocol default initializer

I want to provide a default initializer for a class that:

  • Conforms to ContentType
  • Conforms to Copyable

I'll attempt to answer this question assuming what you mean is that:

  • You want to provide a default implementation of a init(existingContentType: T, newContentTypeId: String?) initializer, as blueprinted in ContentType, if the type conforming to ContentType also conforms to Copyable.

Let's first have a look at your Copyable protocol. It might be due to omission of details/use case in your question, but I don't see the need of the associated type T here, as the protocol basically blueprints/promises "the class type conforming to Copyable, say e.g. TheClassType, will provide an implementation of the static copy(from: TheClassType, to: TheClassType) function" — where the conforming type (TheClassType) is just Self. I.e.:

protocol Copyable: class {
static func copy(from: Self, to: Self)
}

The same holds for ContentType: is there a need for an associatedType here, or is the ContentType of a given concrete type simply the type itself; i.e. Self? Before continuing, lets strip away the parts not relevant to your question:

protocol ContentType {
var contentTypeId: String? { get }
}

Now, prior for any initializer to copy (not assign - we are dealing with reference types) anything into self (e.g. value type members of self), self must have been initialized (or assigned to). So to allow providing a default implementation for a init(existingContentType: Self, newContentTypeId: String?) initializer of ContentType (in case of conformance to Copyable; Self is a class type)—where the implementation is intended to make use of the blueprinted copy(from: Self, to: Self) of copyable—the type conforming to ContentType must know of a way to initializer itself prior to the copying step. I.e., the ContentType needs to blueprint some initializer that can be used to initialize self in the "copying initializer" prior to invoking the copy(from:to) method. Let's simply blueprint init():

protocol ContentType {
var contentTypeId: String? { get set }
init()
}

Now, as the ContentType blueprints a contentTypeId member, and the copying initializer contains a newContentTypeId parameter, it could be sensible to provide a default implemented initializer with contentTypeId as its only parameter; namely init(contentTypeId: String?):

extension ContentType {
init(contentTypeId: String?) {
self.init()
self.contentTypeId = contentTypeId
}
}

With this in place, we can provide the default implemented init(existingContentType: Self, newContentTypeId: String?) initializer as a constrained extension to ContentType based on conformance of Self to Copyable:

extension ContentType where Self: Copyable {
init(existingContentType: Self, newContentTypeId: String?) {
self.init(contentTypeId: newContentTypeId)
Self.copy(from: existingContentType, to: self)
}
}

Example

Putting the above together:

protocol Copyable: class {
static func copy(from: Self, to: Self)
}

protocol ContentType {
var contentTypeId: String? { get set }
init()
}

extension ContentType {
init(contentTypeId: String?) {
self.init()
self.contentTypeId = contentTypeId
}
}

extension ContentType where Self: Copyable {
init(existingContentType: Self, newContentTypeId: String?) {
self.init(contentTypeId: newContentTypeId)
Self.copy(from: existingContentType, to: self)
}
}

Example:

// Copyable content type
final class Foo: ContentType, Copyable {
// Note that since all stored properties have initial values,
// the compiler provides a synthesized initializer for init().
var contentTypeId: String?
var data = 0

static func copy(from: Foo, to: Foo) {
to.data = from.data
}
}

let foo1 = Foo(contentTypeId: "foo1")
foo1.data = 42
let foo2 = Foo(existingContentType: foo1, newContentTypeId: "foo2")

print(foo1.contentTypeId ?? "None", foo1.data) // foo1 42
print(foo2.contentTypeId ?? "None", foo2.data) // foo2 42

// Non-copyable content type
final class Bar: ContentType {
var contentTypeId: String?
} // Bar has no access to
// init(existingContentType: Self, newContentTypeId: String?)


Related Topics



Leave a reply



Submit