[swift-users] ⁨Is it possible to store a set of heterogeneous items with protocol?

Howard Lovatt howard.lovatt at gmail.com
Tue Jul 11 19:07:44 CDT 2017


I would be tempted to use classes for this if you can use single
inheritance. If you need multiple inheritance then use an enum and hand
code the dispatch, a lot more work :(. E.G.:

protocol A {
    func a() -> String
}
protocol B {
    func b() -> String
}
struct AB1: A, B, Hashable {
    func a() -> String {
        return "AB1.a"
    }
    func b() -> String {
        return "AB1.b"
    }
    var hashValue: Int {
        return 1
    }
    static func ==(lhs: AB1, rhs: AB1) -> Bool {
        return true
    }
}
struct AB2: A, B, Hashable {
    func a() -> String {
        return "AB2.a"
    }
    func b() -> String {
        return "AB2.b"
    }
    var hashValue: Int {
        return 2
    }
    static func ==(lhs: AB2, rhs: AB2) -> Bool {
        return true
    }
}
enum AB1Or2: A, B, Hashable {
    case ab1(AB1)
    case ab2(AB2)
    func a() -> String {
        switch self {
        case .ab1(let ab1Arg):
            return ab1Arg.a()
        case .ab2(let ab2Arg):
            return ab2Arg.a()
        }
    }
    func b() -> String {
        switch self {
        case .ab1(let ab1Arg):
            return ab1Arg.b()
        case .ab2(let ab2Arg):
            return ab2Arg.b()
        }
    }
    var hashValue: Int {
        switch self {
        case .ab1(let ab1Arg):
            return ab1Arg.hashValue
        case .ab2(let ab2Arg):
            return ab2Arg.hashValue
        }
    }
    static func ==(lhs: AB1Or2, rhs: AB1Or2) -> Bool {
        switch lhs {
        case .ab1(let lhsAB1):
            switch rhs {
            case .ab1(let rhsAB1):
                return lhsAB1 == rhsAB1
            default:
                return false
            }
        case .ab2(let lhsAB2):
            switch rhs {
            case .ab2(let rhsAB2):
                return lhsAB2 == rhsAB2
            default:
                return false
            }
        }
    }
}
let ab1s = Set([AB1Or2.ab1(AB1())])
let ab2s = Set([AB1Or2.ab2(AB2())])
let abs = ab1s.union(ab2s)


On Tue, 11 Jul 2017 at 10:46 pm, Glen Huang <heyhgl at gmail.com> wrote:

> Thanks for bringing AnyHashable to my attention.
>
> It works, but the types are now erased. I want to have a union of the two
> sets because I want to loop over it to treat each contained item as Named,
> so I can process them as though they are of the same type. Is this type of
> use case really should be addressed using super class?
>
> On 11 Jul 2017, at 7:38 PM, Howard Lovatt <howard.lovatt at gmail.com> wrote:
>
> You can have a set of AnyHashable:
>
> var item = Set<AnyHashable>()
> item.insert(AnyHashable(Foo()))
> item.insert(AnyHashable(Bar()))
>
>
> Depends what you will do with the set if this is viable or not. You can
> also use classes and ObjectID.
>
> You might want this though:
>
> var item = [AnyHashable: Any]
>
> extension Dictionary where Key == AnyHashable, Value: Hashable {
>     func insert(_ value: Value) {
>         self[AnyHashable(value)] == value
>     }
> }
>
> item.insert(Foo())
> item.insert(Bar())
>
>
> So you get at the stored value.
>
> -- Howard.
>
> On 11 Jul 2017, at 8:09 pm, Glen Huang via swift-users <
> swift-users at swift.org> wrote:
>
> Hi,
>
> I want to store some heterogeneous items all conform to a protocol inside
> a set, is it something possible to do in swift?
>
> I tried this example:
>
> ```
> protocol Named: Hashable {
>   var name: String { get }
> }
>
> extension Named {
>   var hashValue: Int {
>       return name.hashValue
>   }
>
>   static func ==(lhs: Self, rhs: Self) -> Bool {
>       return lhs.name == rhs.name
>   }
> }
>
> struct Foo: Named {
>   var name = "foo"
> }
>
> struct Bar: Named {
>   var name = "bar"
> }
>
> var item = Set<Named>()
> item.insert(Foo())
> item.insert(Bar())
> ```
>
> But it failed at `Set<Named>()` where it complained "Using 'Named' as a
> concrete type conforming to protocol 'Hashable' is not supported”.
>
> After watching the WWDC session "Protocol-Oriented Programming in Swift”
> by Dave Abrahams, I try to use protocols whenever possible. But I can’t
> seem to overcome this barrier. Set.Element must confirm to Hashable, which
> inherits from Equatable, which has self requirement, which ultimately means
> that Set.Element all must be of the same type. So it seems it’s impossible
> to have heterogeneous items using protocol. Is that the case?
>
> My use case is this:
>
> I have an object that can contain two sets of other objects:
>
> ```
> class Parent {
>   var foos: Set<Foo>
>   var bars: Set<Bar>
> }
> ```
>
> I want to define a computed property “all” that is the union of the two
> sets. Foo and Bar conform to the same protocol. I wonder what return type I
> should use for the union? Do I have to go back to OOP and define a super
> class for Foo and Bar?
>
> Thanks.
> _______________________________________________
> swift-users mailing list
> swift-users at swift.org
> https://lists.swift.org/mailman/listinfo/swift-users
>
>
> --
-- Howard.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-users/attachments/20170712/aa9ca812/attachment.html>


More information about the swift-users mailing list