[swift-users] Is it possible to store a set of heterogeneous items with protocol?
Vladimir.S
svabox at gmail.com
Tue Jul 18 12:33:10 CDT 2017
On 17.07.2017 4:51, Glen Huang via swift-users wrote:
> Thanks for the code sample and link, but if I’m not wrong, this pattern doesn’t allow
> heterogeneous items.
Support the question. Trying to understand if we can have something like
[AnyHashable] for our custom protocol(with associated type) or AnyHashable has a very
special support from compiler and we can use only [Any] or such kind of wrapper:
struct AnyMyProtocol {
let actualInstance: Any
init<T: MyProtocol>(_ instance: T) { actualInstance = instance}
}
let instances: [AnyMyProtocol] = [AnyMyProtocol(...), AnyMyProtocol(...)]
if let some = instances[0].actualInstance as? SpecificImplementationOfMyProtocol {
// use 'some' as SpecificImplementationMyProtocol instance
// seems like no way to refer to just MyProtocol
}
>
> If I have these definitions:
>
> struct Chicken {}
> struct Pig {}
>
> class ChickenFarm: Farm {
> func grow() -> Chicken {
> return Chicken()
> }
> }
>
> class PigFarm: Farm {
> func grow() -> Pig {
> return Pig()
> }
> }
>
> Then:
>
> var farms = // How do I define a set that can contain both ChickenFarm and PigFarm?
> farms.insert(AnyFarm<Chicken>(ChickenFarm()))
> farms.insert(AnyFarm<Pig>(PigFarm()))
>
>
>> On 17 Jul 2017, at 4:02 AM, Nevin Brackett-Rozinsky
>> <nevin.brackettrozinsky at gmail.com <mailto:nevin.brackettrozinsky at gmail.com>> wrote:
>>
>> The standard pattern for type-erasure in Swift looks like this:
>>
>> protocol Farm {
>> associatedtype Produce
>> func grow() -> Produce
>> }
>>
>> private class _AnyFarmBase<T> : Farm {
>> func grow() -> T { fatalError() }
>> }
>>
>> private final class _AnyFarmBox<U: Farm>: _AnyFarmBase<U.Produce> {
>> var farm: U
>> init(_ x: U) { farm = x }
>> override func grow() -> U.Produce {
>> return farm.grow()
>> }
>> }
>>
>> public final class AnyFarm<V> : Farm {
>> private let wrapped: _AnyFarmBase<V>
>> func grow() -> V { return wrapped.grow() }
>> init<W: Farm> (_ x: W) where W.Produce == V {
>> wrapped = _AnyFarmBox(x)
>> }
>> }
>>
>>
>> There is one little hiccough when you need an initializer in the abstract base
>> class, which you can read about here
>> <https://www.bignerdranch.com/blog/breaking-down-type-erasures-in-swift/> among
>> other places.
>>
>> Hope that helps,
>>
>> Nevin
>>
>>
>> On Sun, Jul 16, 2017 at 12:32 AM, Glen Huang via swift-users <swift-users at swift.org
>> <mailto:swift-users at swift.org>> wrote:
>>
>> This sounds like the right approach!
>>
>> However, as I experimented with AnyHashable more, I found out that after
>> converting a concrete type to it, I could still convert back using “as”:
>>
>> AnyHashable(Foo()) as! Foo
>>
>> I guess that’s not the case with AnyNamed? I tried to imitate AnyHashable:
>>
>> struct AnyNamed: Named {
>> let base: Any
>> init<T: Named>(_ value: T) {
>> base = value
>> }
>>
>> var name: String {
>> // How do I convert `base` to `Named` here?
>> }
>> }
>>
>> But I have no idea what to put in `var name: String`. Also, even if we managed
>> to come up with a solution, would it magically allow direct casting with “as”?
>> Does the complier do something special for AnyHashable?
>>
>>
>> > On 16 Jul 2017, at 12:58 AM, Ole Begemann <ole at oleb.net
>> <mailto:ole at oleb.net>> wrote:
>> >
>> > One way to do this in Swift is a method called type erasure.
>> >
>> > Type erasure means you create a new type that wraps any value whose concrete
>> type you want to erase. This new type also conforms to the protocol. By
>> convention the type is named Any... (compare AnyIterator and AnySequence in the
>> standard library, which do the same thing).
>> >
>> > struct AnyNamed: Named {
>> > private let _name: () -> String
>> >
>> > init<T: Named>(_ value: T) {
>> > _name = { value.name <http://value.name/> }
>> > }
>> >
>> > var name: String {
>> > return _name()
>> > }
>> > }
>> >
>> > AnyNamed is initialized with a generic value T: Named. Notice that the
>> initializer is generic, but the type itself isn't. Because AnyNamed can't store
>> value: T directly (then it would have to be generic over T), we create a
>> closure over value.name <http://value.name/> and store that instead.
>> >
>> > Now we can create a Set<AnyNamed> and, because AnyNamed conforms to Named,
>> treat the set's elements as values conforming to Named:
>> >
>> > var set = Set<AnyNamed>()
>> > set.insert(AnyNamed(Foo()))
>> > set.insert(AnyNamed(Bar()))
>> >
>> > for element in set {
>> > print(element.name <http://element.name/>)
>> > print(element.hashValue)
>> > }
>> >
>> >
>> > On 11.07.2017 12:10, Glen Huang via swift-users 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>
>> >
>> >
>>
>> _______________________________________________
>> 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>
>>
>>
>
>
>
> _______________________________________________
> swift-users mailing list
> swift-users at swift.org
> https://lists.swift.org/mailman/listinfo/swift-users
>
More information about the swift-users
mailing list