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

Glen Huang heyhgl at gmail.com
Wed Jul 12 00:25:24 CDT 2017


Thanks for the detailed example.

It makes sense. But the code can grow pretty quickly with just a few methods and a few concrete types. Also most are repetitive. I’ll try to incorporate this pattern when the use case is simple. But for more complex ones I guess I have to stick with classes for now.

> On 12 Jul 2017, at 8:07 AM, Howard Lovatt <howard.lovatt at gmail.com> wrote:
> 
> 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 <mailto: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 <mailto: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 <mailto: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 <http://lhs.name/> == rhs.name <http://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 <mailto:swift-users at swift.org>
>>> https://lists.swift.org/mailman/listinfo/swift-users <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/5c5fb7db/attachment.html>


More information about the swift-users mailing list