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
> <[email protected]> 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
> <[email protected] <mailto:[email protected]>> 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 <[email protected]
> > <mailto:[email protected]>> 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
> >> [email protected] <mailto:[email protected]>
> >> https://lists.swift.org/mailman/listinfo/swift-users
> >> <https://lists.swift.org/mailman/listinfo/swift-users>
> >
> >
>
> _______________________________________________
> swift-users mailing list
> [email protected] <mailto:[email protected]>
> https://lists.swift.org/mailman/listinfo/swift-users
> <https://lists.swift.org/mailman/listinfo/swift-users>
>
_______________________________________________
swift-users mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-users