Conditional Conformance: Using 'T' as a concrete type conformance to 'T' is not supported [duplicate] - swift

This question already has answers here:
Protocol doesn't conform to itself?
(3 answers)
Closed 4 years ago.
I have some very basic code as I'm trying to learn about conditional conformance:
protocol Animal {
var name: String { get }
}
protocol Social {
func speak()
}
class Cat: Animal, Social {
var name: String
init(name: String) {
self.name = name
}
func speak() {
print("Meow")
}
}
class Dog: Animal, Social {
var name: String
init(name: String) {
self.name = name
}
func speak() {
print("Ruff")
}
}
extension Array: Social where Element: Social {
func speak() {
forEach { $0.speak() }
}
}
let array: [Social] = [Dog(name: "Rocco"), Cat(name: "Gozer")]
array.speak()
When trying to execute the last line array.speak(), I get the error:
"Using 'Social' as a concrete type conforming to protocol 'Social' is not supported."
The way I read the line where I extend Array is this:
The array conforms to protocol Social if all elements in the array conform to Social. Is this correct? The following works just fine:
array.forEach { social in
social.speak()
}
Which makes me believe I've done things right, as far as declaring/initializing the array.
I've looked at multiple posts regarding conditional conformance, but none just give me the basics on how to declare the array (if that's my problem) in order to use it and conform to the protocol Social.
I'm currently reading the book "Swift Apprentice" by Ray Wenderlich and they show everything except actually using the code in an example.
Any help would be greatly appreciated

Change the extension to Element is rather than Element conforms to
extension Array where Element == Social {
func speak() {
forEach { $0.speak() }
}
}
The constraint Array : Social is irrelevant.

A protocol does not conform to itself. Element: Social means that Element must be a type that conforms to Social. Social is not a type that conforms to Social. (Imagine if Social included an init() requirement. What class would Social() return? This is why Social does not conform to itself.)
In this particular case, you probably want both [Social] and [Dog] to get speak(), and in that case you (somewhat unfortunately) have to say so by duplicating the extension, once for Element == Social and once for Element: Social.

Related

How can I extend an array where the element is an array that has an element that conforms to a protocol

I'd like to create a matrix where the element in the cell conforms to a protocol.
Here is kind of how I envision it being implemented, but I'm getting stuck in the syntax.
extension Array where Element == Array<T>, where T: MyProtocol {
}
You can constrain your Collection's Element to Collection and constrain its Element.Element to your protocol:
protocol MyProtocol { }
extension Collection where Element: Collection, Element.Element: MyProtocol {
}
The generalized form of your question is, "How can I extend a generic type which has a placeholder that is also a generic type?". (The protocol part is not actually a part of your problem.)
The answer, as I showed in the other question I linked you to, is that you can't do it at the extension level. You have to do it at the method/subscript/initializer level.
It doesn't matter if the placeholder is replaced by the extended type, or not. Regardless, there's never a way to refer to a placeholder of a placeholder, in an extension declaration.
struct GenericType<PlaceHolder> { }
extension Array {
func ƒ<🤷>() where Element == GenericType<🤷> { }
subscript<🪆>(_: 🪆.Type = 🪆.self) -> Void
where Element == Array<🪆> {
get { }
}
}
extension GenericType {
init?<🧃>() where PlaceHolder == [🧃] {
nil
}
}
If you need a property, then you'll need to use at least one protocol, as Leo shows in his answer.

Can I get the name of a type conforming to a protocol from that protocol?

I would like to know if I can find the name of a type conforming to a given protocol, from that protocol. I was thinking of protocol extension to avoid repetition in every type conforming to that protocol. I tried this:
protocol T {
var type: String { get }
}
extension T {
var type: String {
return String(describing: T.self)
}
}
struct S: T {}
let s = S()
print(s.type)
But this is showing T instead of S.
Is there any way I can do this?
Naturally it's printing T, that's what you asked for with String(describing: T.self). T is always the protocol itself.
Inside the protocol extension Self (capital 'S') is how you refer to the conforming type.
So the extension should be:
extension T {
var typeName: String {
return String(describing: Self.self)
}
}
Aside, the built-in type(of:) function already gives you the dynamic type of any object, so it's not clear that you really need to duplicate this functionality on your own.

Working with generic constraints

I know this question has been asked before but I have no idea how to solve this current problem. I have defined a protocol MultipleChoiceQuestionable with an associatedtype property:
protocol Questionable {
var text: String {get set}
var givenAnswer: String? {get set}
}
protocol MultipleChoiceQuestionable: Questionable {
associatedtype Value
var answers: Value { get }
}
struct OpenQuestion: Questionable {
var text: String
var givenAnswer: String?
}
struct MultipleChoiceQuestion: MultipleChoiceQuestionable {
typealias Value = [String]
var text: String
var givenAnswer: String?
var answers: Value
}
struct NestedMultipleChoiceQuestion: MultipleChoiceQuestionable {
typealias Value = [MultipleChoiceQuestion]
var text: String
var answers: Value
var givenAnswer: String?
}
Types which conform to this protocol are saved in an array as Questionable like so:
// This array contains OpenQuestion, MultipleChoiceQuestion and NestedMultipleChoiceQuestion
private var questions: [Questionable] = QuestionBuilder.createQuestions()
Somewhere in my code I want to do something like:
let question = questions[index]
if let question = question as? MultipleChoiceQuestionable {
// Do something with the answers
question.answers = .....
}
This is not possible because Xcode warns me: Protocol MultipleChoiceQuestionable can only be used as a generic constraint. I've been searching around on how to solve this issue since generics are quite new for me. Apparently Swift doesn't know the type of the associatedtype during compile time which is the reason this error is thrown. I've read about using type erasure but I don't know if that solves my problem. Maybe I should use generic properties instead or perhaps my protocols are defined wrong?
If the action you want to apply to your sub-protocol objects does not rely on the associated type (i.e. neither has the a generic parameter nor returns the generic type) you can introduce an auxiliary protocol which just exposes the properties/methods you need, let your type conform to that protocol, and declare the question in terms of that protocol.
For example, if you just want to know some info about the question:
protocol MultipleChoiceInfo {
var numberOfAnswers: Int { get }
}
extension MultipleChoiceQuestion: MultipleChoiceInfo {
var numberOfAnswers: Int { return answers.count }
}
// do the same for the other multiple-choice types
Then you can access the questions through the new protocol like this:
let question = questions[index]
if let info = question as? MultipleChoiceInfo {
print(info.numberOfAnswers)
}
As I said, if you cannot provide an abstract (non-generic) interface then this won't work.
EDIT
If you need to process the generic data inside your questions you can extract the logic depending on the concrete generic type into another "processing" type which provides an interface to your questions. Each question type then dispatches its data to the processor interface:
protocol MultipleChoiceProcessor {
func process(stringAnswers: [String])
func process(nestedAnswers: [MultipleChoiceQuestion])
}
protocol MultipleChoiceProxy {
func apply(processor: MultipleChoiceProcessor)
}
extension MultipleChoiceQuestion: MultipleChoiceProxy {
func apply(processor: MultipleChoiceProcessor) {
processor.process(stringAnswers: answers)
}
}
Just create a type conforming to MultipleChoiceProcessor and do the type-check dance again:
if let proxy = question as? MultipleChoiceProxy {
proxy.apply(processor:myProcessor)
}
As an aside, if you don't have more protocols and structs in your real application, you might also just ditch the protocol stuff altogether... for this kind of problem it seems a bit over-engineered.

Constraining Generic Type extensions with inheritance [duplicate]

I would like extend Array to add conformance to a new protocol — but only for arrays whose elements themselves conform to a specific protocol.
More generally, I’d like to have types (whether protocols or concrete types) with type parameters implement a protocol only when the type parameters match certain constraints.
As of Swift 2.0, this appears to be impossible. Is there a way I’m missing?
Example
Suppose we have the Friendly protocol:
protocol Friendly {
func sayHi()
}
We can extend existing types to implement it:
extension String: Friendly {
func sayHi() {
print("Greetings from \(self)!")
}
}
"Sally".sayHi()
We can also extend Array to implement sayHi() when its elements are all Friendly:
extension Array where Element: Friendly {
func sayHi() {
for elem in self {
elem.sayHi()
}
}
}
["Sally", "Fred"].sayHi()
At this point, the type [Friendly] should itself implement Friendly, since it meets the protocol’s requirements. However, this code doesn’t compile:
extension Array: Friendly where Element: Friendly {
func sayHi() {
for elem in self {
elem.sayHi()
}
}
}
The error message is “extension of type 'Array' with constraints cannot have an inheritance clause,” which seems to shut the door definitively on that direct approach.
Is there an indirect workaround? Some clever trick I can use? Perhaps there’s a way that involves extending SequenceType instead of Array?
A working solution would make this code compile:
let friendly: Friendly = ["Foo", "Bar"]
Update: This has landed in Swift 4.1, and it is a thing of beauty!
The extension Array: Friendly where Element: Friendly example now compiles as given in the original question.
EDIT: As noted in the updated question, this is now possible since Swift 4.1
This is not currently possible in Swift (as of Xcode 7.1). As the error indicates, you can't restrict protocol conformance ("inheritance clause") to a type-constrained extension. Maybe someday. I don't believe there's any deep reason for this to be impossible, but it's currently not implemented.
The closest you can get is to create a wrapper type such as:
struct FriendlyArray<Element: Friendly>: Friendly {
let array: [Element]
init(_ array: [Element]) {
self.array = array
}
func sayHi() {
for elem in array {
elem.sayHi()
}
}
}
let friendly: Friendly = FriendlyArray(["Foo", "Bar"])
(You would likely want to extend FriendlyArray to be a CollectionType.)
For a tale of my own descent into the madness of trying to make this work, and my crawl back from the edge, see NSData, My Old Friend.
The good news is that what you are asking for Conditional Conformance is coming in Swift 4.1:
https://swift.org/blog/conditional-conformance/

Is it possible to add type constraints to a Swift protocol conformance extension?

I would like extend Array to add conformance to a new protocol — but only for arrays whose elements themselves conform to a specific protocol.
More generally, I’d like to have types (whether protocols or concrete types) with type parameters implement a protocol only when the type parameters match certain constraints.
As of Swift 2.0, this appears to be impossible. Is there a way I’m missing?
Example
Suppose we have the Friendly protocol:
protocol Friendly {
func sayHi()
}
We can extend existing types to implement it:
extension String: Friendly {
func sayHi() {
print("Greetings from \(self)!")
}
}
"Sally".sayHi()
We can also extend Array to implement sayHi() when its elements are all Friendly:
extension Array where Element: Friendly {
func sayHi() {
for elem in self {
elem.sayHi()
}
}
}
["Sally", "Fred"].sayHi()
At this point, the type [Friendly] should itself implement Friendly, since it meets the protocol’s requirements. However, this code doesn’t compile:
extension Array: Friendly where Element: Friendly {
func sayHi() {
for elem in self {
elem.sayHi()
}
}
}
The error message is “extension of type 'Array' with constraints cannot have an inheritance clause,” which seems to shut the door definitively on that direct approach.
Is there an indirect workaround? Some clever trick I can use? Perhaps there’s a way that involves extending SequenceType instead of Array?
A working solution would make this code compile:
let friendly: Friendly = ["Foo", "Bar"]
Update: This has landed in Swift 4.1, and it is a thing of beauty!
The extension Array: Friendly where Element: Friendly example now compiles as given in the original question.
EDIT: As noted in the updated question, this is now possible since Swift 4.1
This is not currently possible in Swift (as of Xcode 7.1). As the error indicates, you can't restrict protocol conformance ("inheritance clause") to a type-constrained extension. Maybe someday. I don't believe there's any deep reason for this to be impossible, but it's currently not implemented.
The closest you can get is to create a wrapper type such as:
struct FriendlyArray<Element: Friendly>: Friendly {
let array: [Element]
init(_ array: [Element]) {
self.array = array
}
func sayHi() {
for elem in array {
elem.sayHi()
}
}
}
let friendly: Friendly = FriendlyArray(["Foo", "Bar"])
(You would likely want to extend FriendlyArray to be a CollectionType.)
For a tale of my own descent into the madness of trying to make this work, and my crawl back from the edge, see NSData, My Old Friend.
The good news is that what you are asking for Conditional Conformance is coming in Swift 4.1:
https://swift.org/blog/conditional-conformance/