⁨Is it possible to store a set of heterogeneous items with protocol?


(Glen Huang) #1

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.


(Glen Huang) #2

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@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@swift.org <mailto:swift-users@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@swift.org <mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users


(Ole Begemann) #3

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 }
     }

     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 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)
     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 == 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
https://lists.swift.org/mailman/listinfo/swift-users


(Howard Lovatt) #4

You are hardly alone struggling with this, it seems to come up every other
week!

You can write your own custom AnyProtocol type that includes Self, a pain
but doable, 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
    }
}
struct AnyABHashable: A, B, Hashable {
    let equalsClosure: (_ rhs: AnyABHashable) -> Bool
    let hashValueClosure: () -> Int
    let bClosure: () -> String
    let aClosure: () -> String
    static func ==(lhs: AnyABHashable, rhs: AnyABHashable) -> Bool {
        return lhs.equalsClosure(rhs)
    }
    var hashValue: Int {
        return hashValueClosure()
    }
    func b() -> String {
        return bClosure()
    }
    func a() -> String {
        return aClosure()
    }
}
// AB1 init
extension AnyABHashable {
    init(_ ab1: AB1) {
        equalsClosure = { (r) in
            if let rhs = r as? AB1 {
                return ab1 == rhs
            }
            return false
        }
        hashValueClosure = { return ab1.hashValue }
        aClosure = { return ab1.a() }
        bClosure = { return ab1.b() }
    }
}
// AB2 init
extension AnyABHashable {
    init(_ ab2: AB2) {
        equalsClosure = { (r) in
            if let rhs = r as? AB2 {
                return ab2 == rhs
            }
            return false
        }
        hashValueClosure = { return ab2.hashValue }
        aClosure = { return ab2.a() }
        bClosure = { return ab2.b() }
    }
}
let ab1Set = Set([AnyABHashable(AB1())])
let ab2Set = Set([AnyABHashable(AB2())])
let abSet = ab1Set.union(ab2Set)
for ab in abSet {
    ab.a()
    ab.b()
}

···

On Tue, 11 Jul 2017 at 8:10 pm, Glen Huang via swift-users < swift-users@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@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

--
-- Howard.


(Adrian Zubarev) #5

If the solution you seek is not designed so that the module user can extend the set of types then you could wrap your types into enum cases and use the enum for your set. :wink: When Swift will support anonymous enum cases, this will be an elegant solution to these type of things.

···

--
Adrian Zubarev
Sent with Airmail

Am 11. Juli 2017 um 14:46:12, Glen Huang via swift-users (swift-users@swift.org(mailto:swift-users@swift.org)) schrieb:

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@gmail.com(mailto:howard.lovatt@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@swift.org(mailto:swift-users@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@swift.org(mailto:swift-users@swift.org)
> > https://lists.swift.org/mailman/listinfo/swift-users

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


(Glen Huang) #6

This sounds pretty interesting.

But I can’t totally wrap my head around it. How do I "wrap types into enum cases”? Could you provide a sample code? Thanks.

···

On 11 Jul 2017, at 8:50 PM, Adrian Zubarev <adrian.zubarev@devandartist.com> wrote:

If the solution you seek is not designed so that the module user can extend the set of types then you could wrap your types into enum cases and use the enum for your set. :wink: When Swift will support anonymous enum cases, this will be an elegant solution to these type of things.

--
Adrian Zubarev
Sent with Airmail
Am 11. Juli 2017 um 14:46:12, Glen Huang via swift-users (swift-users@swift.org <mailto:swift-users@swift.org>) schrieb:

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@gmail.com <mailto:howard.lovatt@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@swift.org <mailto:swift-users@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@swift.org <mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users

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


(Howard Lovatt) #7

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@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@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@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@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

--

-- Howard.


(Glen Huang) #8

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@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 }
   }

   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 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)
   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 == 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
https://lists.swift.org/mailman/listinfo/swift-users


(Glen Huang) #9

NM, I think you meant this?

enum Either<T1, T2> {
  case Left(T1)
  case Right(T2)
}

···

On 11 Jul 2017, at 9:06 PM, Glen Huang via swift-users <swift-users@swift.org> wrote:

This sounds pretty interesting.

But I can’t totally wrap my head around it. How do I "wrap types into enum cases”? Could you provide a sample code? Thanks.

On 11 Jul 2017, at 8:50 PM, Adrian Zubarev <adrian.zubarev@devandartist.com <mailto:adrian.zubarev@devandartist.com>> wrote:

If the solution you seek is not designed so that the module user can extend the set of types then you could wrap your types into enum cases and use the enum for your set. :wink: When Swift will support anonymous enum cases, this will be an elegant solution to these type of things.

--
Adrian Zubarev
Sent with Airmail
Am 11. Juli 2017 um 14:46:12, Glen Huang via swift-users (swift-users@swift.org <mailto:swift-users@swift.org>) schrieb:

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@gmail.com <mailto:howard.lovatt@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@swift.org <mailto:swift-users@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@swift.org <mailto:swift-users@swift.org>
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

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


(Glen Huang) #10

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@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@gmail.com <mailto:heyhgl@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@gmail.com <mailto:howard.lovatt@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@swift.org <mailto:swift-users@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@swift.org <mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users

--
-- Howard.


#11

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> 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@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 }
> }
>
> 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 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)
> 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 == 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
>> https://lists.swift.org/mailman/listinfo/swift-users
>
>

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


(Adrian Zubarev) #12

No it does not have to be a generic enum at all, as long you do not want extending the types from a diffrent module. Simply add a new enum case for each type you need, like `case string(String)`. You may also want to ask the wrapped type for it's hashValue so that it will hash correctly.

···

--
Adrian Zubarev
Sent with Airmail

Am 11. Juli 2017 um 15:15:11, Glen Huang (heyhgl@gmail.com(mailto:heyhgl@gmail.com)) schrieb:

NM, I think you meant this?

enum Either<T1, T2> {
case Left(T1)
case Right(T2)
}

> On 11 Jul 2017, at 9:06 PM, Glen Huang via swift-users <swift-users@swift.org(mailto:swift-users@swift.org)> wrote:
> This sounds pretty interesting.
>
> But I can’t totally wrap my head around it. How do I "wrap types into enum cases”? Could you provide a sample code? Thanks.
>
> > On 11 Jul 2017, at 8:50 PM, Adrian Zubarev <adrian.zubarev@devandartist.com(mailto:adrian.zubarev@devandartist.com)> wrote:
> > If the solution you seek is not designed so that the module user can extend the set of types then you could wrap your types into enum cases and use the enum for your set. :wink: When Swift will support anonymous enum cases, this will be an elegant solution to these type of things.
> >
> > --
> > Adrian Zubarev
> > Sent with Airmail
> >
> > Am 11. Juli 2017 um 14:46:12, Glen Huang via swift-users (swift-users@swift.org(mailto:swift-users@swift.org)) schrieb:
> >
> > >
> > > 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@gmail.com(mailto:howard.lovatt@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@swift.org(mailto:swift-users@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@swift.org(mailto:swift-users@swift.org)
> > > > > 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
>
> _______________________________________________
> swift-users mailing list
> swift-users@swift.org(mailto:swift-users@swift.org)
> https://lists.swift.org/mailman/listinfo/swift-users


(Glen Huang) #13

On second thought, this doesn’t seem to solve the problem. Because once I extract the value out, they’re of different types. I can’t uniformly treat them as Named and process them.

Unless I got the enum definition wrong.

Sorry for the quick-fire mails.

···

On 11 Jul 2017, at 9:15 PM, Glen Huang <heyhgl@gmail.com> wrote:

NM, I think you meant this?

enum Either<T1, T2> {
  case Left(T1)
  case Right(T2)
}

On 11 Jul 2017, at 9:06 PM, Glen Huang via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

This sounds pretty interesting.

But I can’t totally wrap my head around it. How do I "wrap types into enum cases”? Could you provide a sample code? Thanks.

On 11 Jul 2017, at 8:50 PM, Adrian Zubarev <adrian.zubarev@devandartist.com <mailto:adrian.zubarev@devandartist.com>> wrote:

If the solution you seek is not designed so that the module user can extend the set of types then you could wrap your types into enum cases and use the enum for your set. :wink: When Swift will support anonymous enum cases, this will be an elegant solution to these type of things.

--
Adrian Zubarev
Sent with Airmail
Am 11. Juli 2017 um 14:46:12, Glen Huang via swift-users (swift-users@swift.org <mailto:swift-users@swift.org>) schrieb:

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@gmail.com <mailto:howard.lovatt@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@swift.org <mailto:swift-users@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@swift.org <mailto:swift-users@swift.org>
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

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


(Glen Huang) #14

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.brackettrozinsky@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 <ole@oleb.net <mailto:ole@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
>
>

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


(Michael Rogers) #15

Hi, All:

Can someone please enlighten me as to why the first enum works as expected, giving me Melbourne, but the second gives UIModalPresentationStyle rather than fullScreen?

enum City {
    case Melbourne, Chelyabinsk, Bursa
}
let city = City.Bursa

print(city)
// prints "Melbourne"

let cityName = "\(city)" // or `let cityName = String(city)`
// cityName contains "Melbourne"

// Why doesn't the following work?

let presStyle = UIModalPresentationStyle.fullScreen

print(presStyle) // prints UIModalPresentationStyle

let presStyleName = "\(presStyle)"

I have an ugly work-around using String UIPresentationStyle tuples, but I'd rather not use it if possible.

Thanks,

Michael

···

---------------------------------------------------------------------------
Dr. Michael P. Rogers
Associate Professor, Northwest Missouri State University
Tinkerer-in-Chief and Angel Flight Pilot
Twitter & Google+: mprogers@mac.com; Skype: mprogersatmac.com; Facebook: mprogers
Phone: 309-825-6454 (H); 660-562-1551 (W)

Thank you, thank you, thank you, and I'd like to thank all the little people -- Tom Thumb, Ant-Man, and, of course, all the Lilliputians -- who made this award possible :grinning:


(Vladimir) #16

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
}

···

On 17.07.2017 4:51, Glen Huang via swift-users wrote:

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@gmail.com <mailto:nevin.brackettrozinsky@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 <ole@oleb.net >> <mailto:ole@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


(Alex Blewitt) #17

Sure - it's because it's an alias that is imported via Objective-C rather than defined in Swift:

typedef NS_ENUM(NSInteger, UIModalPresentationStyle) {
        UIModalPresentationFullScreen = 0,
        UIModalPresentationPageSheet NS_ENUM_AVAILABLE_IOS(3_2) __TVOS_PROHIBITED,
        UIModalPresentationFormSheet NS_ENUM_AVAILABLE_IOS(3_2) __TVOS_PROHIBITED,
        UIModalPresentationCurrentContext NS_ENUM_AVAILABLE_IOS(3_2),
        UIModalPresentationCustom NS_ENUM_AVAILABLE_IOS(7_0),

(you can see this in the UIViewController.h file in the UIKit, which is found underneath your Xcode install)

The importer notes that the name of such typedef'd constants effectively become translated into an enum as far as Swift source code is concerned, but it's a different kind of case.

Having said that, it might be a bug in the case that the description of the constant isn't the name of the element type. It would be worth raising this on bugs.swift.org and use this example in case there's something that can be done to fix it.

Alex

···

On 17 Jul 2017, at 15:15, Michael Rogers via swift-users <swift-users@swift.org> wrote:

Hi, All:

Can someone please enlighten me as to why the first enum works as expected, giving me Melbourne, but the second gives UIModalPresentationStyle rather than fullScreen?


(Slava Pestov) #18

There’s already a radar filed for this (I don’t know if there’s a JIRA bug though).

The problem is that the standard library’s reflection mechanism only knows how to map the in-memory representation of an enum to a case name for native Swift enums. Objective-C enums are represented by their ‘raw value’, and we don’t store the mapping in a way that’s accessible to the reflection code.

Slava

···

On Jul 17, 2017, at 3:33 PM, Alex Blewitt via swift-users <swift-users@swift.org> wrote:

On 17 Jul 2017, at 15:15, Michael Rogers via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

Hi, All:

Can someone please enlighten me as to why the first enum works as expected, giving me Melbourne, but the second gives UIModalPresentationStyle rather than fullScreen?

Sure - it's because it's an alias that is imported via Objective-C rather than defined in Swift:

typedef NS_ENUM(NSInteger, UIModalPresentationStyle) {
        UIModalPresentationFullScreen = 0,
        UIModalPresentationPageSheet NS_ENUM_AVAILABLE_IOS(3_2) __TVOS_PROHIBITED,
        UIModalPresentationFormSheet NS_ENUM_AVAILABLE_IOS(3_2) __TVOS_PROHIBITED,
        UIModalPresentationCurrentContext NS_ENUM_AVAILABLE_IOS(3_2),
        UIModalPresentationCustom NS_ENUM_AVAILABLE_IOS(7_0),

(you can see this in the UIViewController.h file in the UIKit, which is found underneath your Xcode install)

The importer notes that the name of such typedef'd constants effectively become translated into an enum as far as Swift source code is concerned, but it's a different kind of case.

Having said that, it might be a bug in the case that the description of the constant isn't the name of the element type. It would be worth raising this on bugs.swift.org <http://bugs.swift.org/> and use this example in case there's something that can be done to fix it.

Alex

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


(Jon Shier) #19

Certain Objective-C enums don’t print a useful debug description. I usually work around this by implementing CustomStringConvertible (or the debug version) for the type myself. You probably want to file a bug with Apple.

Jon

···

On Jul 17, 2017, at 6:15 PM, Michael Rogers via swift-users <swift-users@swift.org> wrote:

Hi, All:

Can someone please enlighten me as to why the first enum works as expected, giving me Melbourne, but the second gives UIModalPresentationStyle rather than fullScreen?

enum City {
    case Melbourne, Chelyabinsk, Bursa
}
let city = City.Bursa

print(city)
// prints "Melbourne"

let cityName = "\(city)" // or `let cityName = String(city)`
// cityName contains "Melbourne"

// Why doesn't the following work?

let presStyle = UIModalPresentationStyle.fullScreen

print(presStyle) // prints UIModalPresentationStyle

let presStyleName = "\(presStyle)"

I have an ugly work-around using String UIPresentationStyle tuples, but I'd rather not use it if possible.

Thanks,

Michael
---------------------------------------------------------------------------
Dr. Michael P. Rogers
Associate Professor, Northwest Missouri State University
Tinkerer-in-Chief and Angel Flight Pilot
Twitter & Google+: mprogers@mac.com <mailto:mprogers@mac.com>; Skype: mprogersatmac.com <http://mprogersatmac.com/>; Facebook: mprogers
Phone: 309-825-6454 (H); 660-562-1551 (W)

Thank you, thank you, thank you, and I'd like to thank all the little people -- Tom Thumb, Ant-Man, and, of course, all the Lilliputians -- who made this award possible :grinning:

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


(Jordan Rose) #20

AnyHashable is special, sorry. You'll have to use this sort of indirect unwrapping instead. You can write a little convenience method for it though, if you want:

extension AnyMyProtocol {
  func get<T: MyProtocol>(as: T.Type) -> T? {
    return self.actualInstance as? T
  }
}

if let some = instances[0].get(as: SpecificImplementationOfMyProtocol.self) {
  // use 'some' here
}

Jordan

···

On Jul 18, 2017, at 10:33, Vladimir.S via swift-users <swift-users@swift.org> wrote:

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
}