2

I have a generic protocol:

protocol SectionType {
    associatedtype I: Equatable
    associatedtype T: Equatable
    var info: I? { get }
    var items: [T] { get }
}

and an Array extension for it:

/// Offers additional method(s) to process SectionType list.
extension Array where Element: SectionType {
    /// Dummy comparision method. Implementation is not that relevant but just to employ Equatable.
    /// - Parameter to: Another array of sections.
    /// - Returns: True if first info and first elemements in both arrays exist and both are equal.
    func dummyCompare(with otherArray: [Element]) -> Bool {
        guard
            let first = self.first,
            let firstOther = otherArray.first,
            let firstElement = first.items.first,
            let firstOtherElement = firstOther.items.first,
            let firstInfo = first.info, let firstOtherInfo = firstOther.info
            else { return false }
        return firstInfo == firstOtherInfo && firstElement == firstOtherElement
    }
}

No problem when using with concrete implementations:

Example 1: Works with specific implementation with build-in String type:

Declaration:

struct StringSection: SectionType {
    let info: String?
    let items: [String]
}

Usage:

let stringSection1 = StringSection(info: "Info 1", items: ["Item 1", "Item 2"])
let stringSection2 = StringSection(info: "Info 2", items: ["Item 3", "Item 4"])
let stringSections1 = [stringSection1, stringSection2]
let stringSections2 = [stringSection2, stringSection1]
var areEqual = stringSections1.dummyCompare(with: stringSections2)
print("String section 1 equals 2?: \(areEqual)")

Example 2 - Works with specific implementation with custom types:

Declarations:

protocol SectionInfoType {
    var title: String { get }
}

/// BTW: This is just Swift's stragne way of implementing Equatable protocol for your type:
func == (lhs: SectionInfoType, rhs: SectionInfoType) -> Bool {
    return lhs.title == rhs.title
}

struct SpecificSectionInfo: SectionInfoType, Equatable {
    let title: String
    static func == (lhs: SpecificSectionInfo, rhs: SpecificSectionInfo) -> Bool {
        return lhs.title == rhs.title
    }
}

protocol SectionItemType {
    var text: String { get }
}

/// BTW: This is just Swift's stragne way of implementing Equatable protocol for your type:
func == (lhs: SectionItemType, rhs: SectionItemType) -> Bool {
    return lhs.text == rhs.text
}

struct SpecificSectionItem: SectionItemType, Equatable {
    let text: String
    static func == (lhs: SpecificSectionItem, rhs: SpecificSectionItem) -> Bool {
        return lhs.text == rhs.text
    }
}

struct SpecificSection: SectionType {
    let info: SpecificSectionInfo?
    let items: [SpecificSectionItem]
}

Usage:

let specInfo1 = SpecificSectionInfo(title: "Info 1")
let specItem1 = SpecificSectionItem(text: "Specific item 1")
let specItem2 = SpecificSectionItem(text: "Specific item 2")
let specInfo2 = SpecificSectionInfo(title: "Info 2")
let specItem3 = SpecificSectionItem(text: "Specific item 3")
let specItem4 = SpecificSectionItem(text: "Specific item 4")
let specSection1 = SpecificSection(info: specInfo1, items: [specItem1, specItem2])
let specSection2 = SpecificSection(info: specInfo2, items: [specItem3, specItem4])
let specSections1 = [specSection1, specSection2]
let specSections2 = [specSection2, specSection1]
let areEqual = specSections1.dummyCompare(with: specSections2)
print("Specific section 1 equals 2?: \(areEqual)")

So far, so good, everything works and compiles. But ... I have at least 2 problems with this approach:

Problem 1:

Just from 2 examples above, one can see that this approach needs 'specific' implementation of a SectionType protocol for every combination of info and items type. This seems not so efficient (tremendous amount of code for every each implementation) nor generic.

What I need is a more generalised embodiment of SectionType protocol where types for info and items need to be protocols (provided from external APIs as protocols).

A perfect example (but does not compile):

struct Section: SectionType {
    typealias I = SectionInfoType
    typealias T = SectionItemType
    let info: I?
    let items: [T]
}

Problem 2:

I need to be able to pass it over to other protocol oriented API, f.ex.: as an argument to function like:

func consume(section: SectionType<SectionInfoType, SectionItemType>) {}

But above function declaration produces: Cannot specialize non-generic type 'SectionType' with Xcode proposing a fix: 'Delete <SectionInfoType, SectionItemType>' resulting in this:

func consume(section: SectionType) {}

This does not compile neither and I can not find a way to make it work.

Is it possible to do or is that Swift 3 limitation (I am using Swift 3.1)?

I created Git repository demonstrating those issues. Feel free to collaborate:

https://github.com/lukaszmargielewski/swift3-learning-generics

Hamish
  • 78,605
  • 19
  • 187
  • 280
Lukasz
  • 19,816
  • 17
  • 83
  • 139
  • Why not make `Section` generic such as in your [previous question](http://stackoverflow.com/q/43540206/2976878)? e.g `struct Section : SectionType {...}`. – Hamish Apr 23 '17 at 21:57
  • @Hamish - this actually never fully worked when implementation of a method in array extension actually tried to use `==` (`Equatable` overload function). Note the example from there does not actually use it. When added -it gave compilation error: `SectionInfoType does not conform to the Equatable protocol` even if overload worked outside `Array` extension. That question also did not explore possibilities of specifying inferred types as protocols. – Lukasz Apr 23 '17 at 22:07
  • Your `Array` extension should work fine so long as you're talking in terms of `Element.I` and `Element.T`. Sounds like you're trying to treat a protocol type as a concrete type that conforms to a protocol (this isn't possible, [protocols don't conform to themselves](http://stackoverflow.com/a/43408193/2976878) – solution is usually a type eraser). It would seem to me *that* would be the issue worth pursuing, rather than manually specialising `Section`. (re: downvote, not me!). – Hamish Apr 23 '17 at 22:22
  • Although I'm not sure what's going on with your `SectionInfoType` & `SpecificSectionInfo` relationship, does the protocol actually have a use (do other types conform to it?) – Hamish Apr 23 '17 at 22:22
  • The use of those protocols is mainly 'architectural' - the system it is used in requires me to expose protocols rather than specific implementations. – Lukasz Apr 23 '17 at 22:45
  • Okay, but I still maintain that you're focussing on the wrong problem here – you should be trying to make your code work with a `struct Section : SectionType {...}` rather than trying to specialise it manually. – Hamish Apr 23 '17 at 23:10
  • How about implementation independence and ease of unit testing as returning benefits @RobNapier? Anyways - the question is not "should I do that" but "is it possible" to do? – Lukasz Apr 24 '17 at 05:50
  • 1
    Both of those are achievable with a generic container rather than a protocol. But agreed, that wasn't an answer (which is why I put it in a comment; I wish I had a real answer, but I don't). It may be possible to beat this into submission and create something that compiles. It is not possible to do it in a clean and elegant way in Swift 3. Swift 4 will be a bit better. – Rob Napier Apr 24 '17 at 14:34
  • 1
    For one discussion on how generic structs can be just as flexible as protocols for implementation independence, and often are a better solution in Swift (and definitely Swift 3), see http://www.thedotpost.com/2016/01/rob-napier-beyond-crusty-real-world-protocols – Rob Napier Apr 24 '17 at 14:35

1 Answers1

0

If I understand correctly problem 2 you want your a function that consumes a generic variable that is also specialised, adhering to protocol SectionType. Try:

func consume<Section: SectionType>(section: Section) {
    let foo = section.info
    let foobar = section.items
}
Eppilo
  • 763
  • 6
  • 19