Thanks for the code sample and link, but if I’m not wrong, this pattern doesn’t 
allow heterogeneous items.

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.brackettrozin...@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@swift.org <mailto:swift-users@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 <o...@oleb.net 
> > <mailto:o...@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@swift.org <mailto:swift-users@swift.org>
> >> https://lists.swift.org/mailman/listinfo/swift-users 
> >> <https://lists.swift.org/mailman/listinfo/swift-users>
> >
> >
> 
> _______________________________________________
> swift-users mailing list
> swift-users@swift.org <mailto:swift-users@swift.org>
> https://lists.swift.org/mailman/listinfo/swift-users 
> <https://lists.swift.org/mailman/listinfo/swift-users>
> 

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

Reply via email to