Is it possible to make a Heterogeneous Set in Swift? - swift

Consider this example:
protocol Observable: Hashable {
// ...
}
struct People: Observable {
var name: String
var age: Double
var hashValue: Int {
// ...
}
static func ==(lhs: People, rhs: People) -> Bool {
// ,,,
}
}
struct Color: Observable {
var red: Double, green: Double, blue: Double
var hashValue: Int {
// ...
}
static func ==(lhs: Color, rhs: Color) -> Bool {
// ...
}
}
var observers: Set<Observable> = [] // Not allowed by the compiler
People and Color are both conform to Observable protocol which also inherit from Hashable protocol. I want to store these inside the observers set.
using 'Observable' as a concrete type conforming to protocol
'Hashable' is not supported
Is it possible to do heterogenous Set in Swift?

There is a way to make it possible. (Inspired by Apple's implementation)
Before we begin, this is what we want to build.
protocol Observer: Hashable {
associatedtype Sender: Observable
func valueDidChangeInSender(_ sender: Sender, keypath: String, newValue: Any)
}
The source of this problem is the use of Self that force the array to be Homogenous. You can see it here:
The most important change is that it stop the protocol from being usable as a type.
That makes us can't do:
var observers: [Observer] = [] // Observer is not usable as a type.
Therefore, we need another way to make it work.
We don't do
var observers: [AnyHashable] = []
Because AnyHashable will not constrain the object to conform Observer protocol. Instead, we can wrap the Observer object in the AnyObserver wrapper like this:
var observers: [AnyObserver] = []
observers.append(AnyObserver(yourObject))
This will make sure the value of AnyObserver struct conforms to Observer protocol.
According to WWDC 2015: Protocol-Oriented Programming in Swift, we can make a bridge with isEqual(_:) method so we can compare two Any. This way the object doesn't have to conform to Equatable Protocol.
protocol AnyObserverBox {
var hashValue: Int { get }
var base: Any { get }
func unbox<T: Hashable>() -> T
func isEqual(to other: AnyObserverBox) -> Bool
}
After that, we make the box that conforms to AnyObserverBox.
struct HashableBox<Base: Hashable>: AnyObserverBox {
let _base: Base
init(_ base: Base) {
_base = base
}
var base: Any {
return _base
}
var hashValue: Int {
return _base.hashValue
}
func unbox<T: Hashable>() -> T {
return (self as AnyObserverBox as! HashableBox<T>)._base
}
func isEqual(to other: AnyObserverBox) -> Bool {
return _base == other.unbox()
}
}
This box contains the actual value of the AnyObserver that we will create later.
Finally we make the AnyObserver.
struct AnyObserver {
private var box: AnyObserverBox
public var base: Any {
return box.base
}
public init<T>(_ base: T) where T: Observer {
box = HashableBox<T>(base)
}
}
extension AnyObserver: Hashable {
static func ==(lhs: AnyObserver, rhs: AnyObserver) -> Bool {
// Hey! We can do a comparison without Equatable protocol.
return lhs.box.isEqual(to: rhs.box)
}
var hashValue: Int {
return box.hashValue
}
}
With all of that in place, we can do:
var observers: [AnyObserver] = []
observers.append(AnyObserver(yourObject))

Actually, you cannot declare a Set -or even an array- of type Observable, that's because at some level Observable represents a generic protocol:
Observable -> Hashable -> Equatable:
which contains:
public static func ==(lhs: Self, rhs: Self) -> Bool
That's the reason why of the inability of using it in Heterogenous way. Furthermore, you can't declare an existential type:
var object: Observable?
// error: protocol 'Observable' can only be used as a generic constraint
// because it has Self or associated type requirements
If you are wondering what's the reason of this constraint, I assume that it is logical to compare tow People or tow Color, but not comparing People with Color.
So, what can we do?
As a workaround, you could let your set to be a set of AnyHashable structure (as #Leo mentioned in the comment):
The AnyHashable type forwards equality comparisons and hashing
operations to an underlying hashable value, hiding its specific
underlying type.
As follows:
let people = People(name: "name", age: 101)
let color = Color(red: 101, green: 101, blue: 101)
var observers: Set<AnyHashable> = []
observers.insert(people)
observers.insert(color)
for (index, element) in observers.enumerated() {
if element is People {
print("\(index): people")
}
}
that would be legal.

Related

Use generic struct with protocol

It seems that Swift generics work fine, as long as I don't try to combine them in any practical way. I'm using Swift 4.1, and I would like to create a generic array containing only weak references. I can define this as WeakList<T>. So far so well. But: I would like to use a protocol for T. Swift says nope..
import Foundation
protocol Observer: class {
func stateChanged(sender: SomeClass, newState: Int)
}
struct WeakList<T> where T: AnyObject {
struct Ptr {
weak var p: T?
}
private var storage: [Ptr] = []
var aliveObjects: [T] {
var result: [T] = []
for ptr in storage {
if let p = ptr.p {
result.append(p)
}
}
return result
}
mutating func add(_ obj: T) {
storage.append(Ptr(p: obj))
}
// Let's ignore for a moment that this implementation leaks memory badly.
}
class SomeClass {
var someVar: WeakList<Observer> = WeakList<Observer>()
// Error: WeakList requires that 'Observer' be a class type
var thisIsOk: WeakList<NSObject> = WeakList<NSObject>()
}
(this is not my original code but a minimal verifyable example that contains enough details so that no one can say "just remove the AnyObject constraint from the structure")
I guess what I'm trying to do is just not possible. Or is it? It's just frustrating how 4 out of 5 times when I try to do something with Swift generics, I later learn that what I am trying to do is just not possible. (I can implement the same thing in Objective-C easily, by the way.)
I tried changing the class constraint to an AnyObject constraint => doesn't work either.
I tried to change the AnyObject constraint to a class constraint => doesn't even compile.
And changing it to protocol Observer where Self: NSObject doesn't change anything. NSObject is a class type, Observer is an NSObject. It should follow that Observer is a class type. The "is a" relationship doesn't seem to be transitive here.
With current implementation you cannot inherit the protocol from AnyObject. What you can do is to create a Type Eraser for your protocol and use that instead. Now your type eraser can be inherited from AnyObject.
Something like this:
protocol Observer {
func stateChanged(sender: SomeClass, newState: Int)
}
class AnyObserver: NSObject, Observer {
private let observer: Observer
init(observer: Observer) {
self.observer = observer
}
func stateChanged(sender: SomeClass, newState: Int) {
observer.stateChanged(sender: sender, newState: newState)
}
}
struct WeakList<T> where T: AnyObject {
struct Ptr {
weak var p: T?
}
private var storage: [Ptr] = []
var aliveObjects: [T] {
var result: [T] = []
for ptr in storage {
if let p = ptr.p {
result.append(p)
}
}
return result
}
mutating func add(_ obj: T) {
storage.append(Ptr(p: obj))
}
// Let's ignore for a moment that this implementation leaks memory badly.
}
class SomeClass {
var someVar: WeakList<AnyObserver> = WeakList<AnyObserver>()
var thisIsOk: WeakList<NSObject> = WeakList<NSObject>()
}

Given an instance of a Swift object, can we test its class for the presence of a class function?

I've looked around, and haven't seen an answer to my question (but maybe I should be able to infer one).
I have an object, based on a protocol. It's an associated type protocol, with class operators that are defined, based on the type assigned to associatedtype, like so:
protocol GenericBaseProtocol {
associatedtype T
var myProperty: T {get set}
init(_ myProperty: T )
}
extension GenericBaseProtocol where T: Equatable {
static func ==(lhs: Self, rhs: Self) -> Bool {
return lhs.myProperty == rhs.myProperty
}
}
So if I create a class, based on this, and give T an Equatable type, like so:
class IntClass: GenericBaseProtocol {
typealias T = Int
var myProperty: T = 0
required init(_ myProperty: T ) {
self.myProperty = myProperty
}
}
The resulting object should be comparable, like so:
let lhs = IntClass(3)
let rhs = IntClass(4)
let isEqual = lhs == rhs
Cool. Now, if I then create an instance with a non-Equatable type, like so:
class ArrayClass: GenericBaseProtocol {
typealias T = [String]
var myProperty: T = []
required init(_ myProperty: T ) {
self.myProperty = myProperty
}
}
And instantiate that, like so:
let lhs2A = ArrayClass(["HI"])
let rhs2A = ArrayClass(["Howaya"])
I will have compile-time syntax errors when I try this:
let isEqual = lhs2A == rhs2A
What I'd like to be able to do, is test the class object of lhs2A, and see if it implements static func ==(lhs: Self, rhs: Self) -> Bool
I'm not sure this can be done, but it would be nice for this article I'm writing up if I could add a runtime/guard proof to the playground, instead of simply commenting out the code.
Any ideas?
You could extend your protocol to give it a default implementation for the == operator in cases where the associated type is not Equatable.
This could also be used to provide a runtime indicator of wether the type is equatable or not.
for example:
extension GenericBaseProtocol where T: Equatable {
static func ==(lhs: Self, rhs: Self) -> Bool {
return lhs.myProperty == rhs.myProperty
}
var isEquatable:Bool { return true }
}
extension GenericBaseProtocol {
static func ==(lhs: Self, rhs: Self) -> Bool {
return false
}
var isEquatable:Bool { return false }
}

Swift Declare a protocol equatable

I have a protocol:
protocol CustomProtocol {
var title: String { get }
var subtitle: String { get }
}
Then i have 2 objects, that conform this procotol. And i want to compare them, so i would like to CustomProtocol to be Equatable.
protocol CustomProtocol: Equatable {
var title: String { get }
var subtitle: String { get }
static func ==(lhs: Self, rhs: Self) -> Bool
}
extension CustomProtocol {
static func ==(lhs: Self, rhs: Self) -> Bool {
return lhs.title == rhs.title
}
}
But after that change i get "Protocol CustomProtocol can only be used as a generic constraint because it has Self or associated type requeriments.
The only way i can think to solve this is to have a third property like a hash that depends on the others and compare this property.
Here you have a sample playground with the actual code.
Because Equatable has Self requirements, it should not be implemented directly on a protocol. Otherwise, the protocol will be unusable as a type.
To implement Equatable at the protocol level yet be able to use the protocol as a type, you can use type erasure.
To demonstrate, I have modified the code given in your playground to build a type eraser.
For a detailed explanation of the approach I have used, check out this post on my blog:
https://khawerkhaliq.com/blog/swift-protocols-equatable-part-two/
Here is the modified code from your playground:
protocol CustomProtocol {
var title: String { get }
var subtitle: String { get }
func isEqualTo(_ other: CustomProtocol) -> Bool
func asEquatable() -> AnyEquatableCustomProtocol
}
extension CustomProtocol where Self: Equatable {
func isEqualTo(_ other: CustomProtocol) -> Bool {
guard let o = other as? Self else { return false }
return self == o
}
func asEquatable() -> AnyEquatableCustomProtocol {
return AnyEquatableCustomProtocol(self)
}
}
struct A: CustomProtocol, Equatable {
var title: String
var subtitle: String
static func ==(lhs: A, rhs: A) -> Bool {
return lhs.title == rhs.title && lhs.subtitle == rhs.subtitle
}
}
struct B: CustomProtocol, Equatable {
var title: String
var subtitle: String
static func ==(lhs: B, rhs: B) -> Bool {
return lhs.title == rhs.title && lhs.subtitle == rhs.subtitle
}
}
struct AnyEquatableCustomProtocol: CustomProtocol, Equatable {
var title: String { return value.title }
var subtitle: String { return value.subtitle }
init(_ value: CustomProtocol) { self.value = value }
private let value: CustomProtocol
static func ==(lhs: AnyEquatableCustomProtocol, rhs: AnyEquatableCustomProtocol) -> Bool {
return lhs.value.isEqualTo(rhs.value)
}
}
// instances typed as the protocol
let a: CustomProtocol = A(title: "First title", subtitle: "First subtitle")
let b: CustomProtocol = B(title: "First title", subtitle: "First subtitle")
let equalA: CustomProtocol = A(title: "First title", subtitle: "First subtitle")
let unequalA: CustomProtocol = A(title: "Second title", subtitle: "Second subtitle")
// equality tests
print(a.asEquatable() == b.asEquatable()) // prints false
print(a.asEquatable() == equalA.asEquatable()) // prints true
print(a.asEquatable() == unequalA.asEquatable()) // prints false
The point to note is that with this approach the actual == comparisons are delegated to the underlying concrete types, yet we deal only with protocol types to maintain abstraction.
Here I have used the type erased instances only for one comparison. However, since the type eraser conforms to CustomProtocol these instances can be saved and used in any place where a protocol type is expected. Because they conform to Equatable, they can also be used in any place where Equatable conformance is required.
Just for context, this post explains why it is not advisable to try to implement Equatable conformance or even == functions directly on protocols:
https://khawerkhaliq.com/blog/swift-protocols-equatable-part-one/
Hence the type erasure.
Hope this helps.
The Equatable protocol has a self constraint to solve the problem that you only should be able to check equality between objects of the same type, not the same protocol. That's why it has a self-requirement. Otherwise you could just say
let a: Equatable = 42
let b: Equatable = "hello"
and a == b would work. This would be bad because you could compare objects of totally unrelated types. The self-requirement makes this a compile time error.
If you want to compare your objects on a protocol basis, just implement the == operator without a self-requirement:
extension CustomProtocol {
func == (lhs: CustomProtocol, rhs: CustomProtocol) -> Bool {
return lhs.name == rhs.name
}
func != (lhs: CustomProtocol, rhs: CustomProtocol) -> Bool {
return !(lhs == rhs)
}
}
Now you can declare instances of your protocol directly with the CustomProtocol type and compare them.
But maybe the protocol is not the right abstraction in this case. Maybe you should implement this as an abstract class.
The problem is the rhs parameter is not the same type of the lhs. They just conform to the same protocol.
You could solve that by using a generic type as the second parameter.
exension CustomProtocol {
static func ==<T: CustomProtocol>(lhs: Self, rhs: T) -> Bool {
return lhs.title == rhs.title
}
}

Array of protocol type

I have checked all answers about this problem on stackoverflow, but still can not figure out how to fix this.
My model looks like this
protocol Commandable: Equatable {
var condition: Condition? {get set}
func execute() -> SKAction
}
And 3 structs which implement this protocol
struct MoveCommand: Commandable {
var movingVector: CGVector!
//MARK: - Commandable
var condition: Condition?
func execute() -> SKAction {
...
}
}
extension MoveCommand {
// MARK:- Equatable
static func ==(lhs: MoveCommand, rhs: MoveCommand) -> Bool {
return lhs.movingVector == rhs.movingVector && lhs.condition == rhs.condition
}
}
struct RotateCommand: Commandable {
var side: RotationSide!
// MARK: - Commandable
var condition: Condition?
func execute() -> SKAction {
...
}
}
extension RotateCommand {
// MARK: - Equatable
static func ==(lhs: RotateCommand, rhs: RotateCommand) -> Bool {
return lhs.side == rhs.side && lhs.condition == rhs.condition
}
}
The problems start when I am trying to create third structure which has array of [Commandable]:
struct FunctionCommand: Commandable {
var commands = [Commandable]()
The compiler output: Protocol 'Commandable' can only be used as a generic constraint because it has Self or associated type requirements. Then i rewrote my struct in this way:
struct FunctionCommand<T : Equatable>: Commandable {
var commands = [T]()
I resolve this problem but new problem has appeared. Now i can't create FunctionCommand with instances of Rotate and Move command, only with instances of one of them :( :
let f = FunctionCommand(commands: [MoveCommand(movingVector: .zero, condition: nil),
RotateCommand(side: .left, condition: nil)], condition: nil)
Any Help would be appreciated.
Update: That article helped me to figure out - https://krakendev.io/blog/generic-protocols-and-their-shortcomings
What you need to do is to use type erasure, much like AnyHashable does in the Swift Standard Library.
You can't do:
var a: [Hashable] = [5, "Yo"]
// error: protocol 'Hashable' can only be used as a generic constraint because it has Self or associated type requirements
What you have to do is to use the type-erased type AnyHashable:
var a: [AnyHashable] = [AnyHashable(5), AnyHashable("Yo")]
a[0].hashValue // => shows 5 in a playground
So your solution would be to first split the protocol in smaller parts and promote Equatable to Hashable (to reuse AnyHashable)
protocol Conditionable {
var condition: Condition? { get set }
}
protocol Executable {
func execute() -> SKAction
}
protocol Commandable: Hashable, Executable, Conditionable {}
Then create an AnyCommandable struct, like this:
struct AnyCommandable: Commandable, Equatable {
var exeBase: Executable
var condBase: Conditionable
var eqBase: AnyHashable
init<T: Commandable>(_ commandable: T) where T : Equatable {
self.condBase = commandable
self.exeBase = commandable
self.eqBase = AnyHashable(commandable)
}
var condition: Condition? {
get {
return condBase.condition
}
set {
condBase.condition = condition
}
}
var hashValue: Int {
return eqBase.hashValue
}
func execute() -> SKAction {
return exeBase.execute()
}
public static func ==(lhs: AnyCommandable, rhs: AnyCommandable) -> Bool {
return lhs.eqBase == rhs.eqBase
}
}
And then you can use it like this:
var a = FunctionCommand()
a.commands = [AnyCommandable(MoveCommand()), AnyCommandable(FunctionCommand())]
And you can easily access properties of commands, because AnyCommandable implements Commandable
a.commands[0].condition
You need to remember to now add Hashable and Equatable to all your commands.
I used those implementations for testing:
struct MoveCommand: Commandable {
var movingVector: CGVector!
var condition: Condition?
func execute() -> SKAction {
return SKAction()
}
var hashValue: Int {
return Int(movingVector.dx) * Int(movingVector.dy)
}
public static func ==(lhs: MoveCommand, rhs: MoveCommand) -> Bool {
return lhs.movingVector == rhs.movingVector
}
}
struct FunctionCommand: Commandable {
var commands = [AnyCommandable]()
var condition: Condition?
func execute() -> SKAction {
return SKAction.group(commands.map { $0.execute() })
}
var hashValue: Int {
return commands.count
}
public static func ==(lhs: FunctionCommand, rhs: FunctionCommand) -> Bool {
return lhs.commands == rhs.commands
}
}
I think it can be easily done by introduction of your own CustomEquatable protocol.
protocol Commandable: CustomEquatable {
var condition: String {get}
}
protocol CustomEquatable {
func isEqual(to: CustomEquatable) -> Bool
}
Then, you objects have to conform to this protocol and additionally it should conform Equitable as well.
struct MoveCommand: Commandable, Equatable {
let movingVector: CGRect
let condition: String
func isEqual(to: CustomEquatable) -> Bool {
guard let rhs = to as? MoveCommand else { return false }
return movingVector == rhs.movingVector && condition == rhs.condition
}
}
struct RotateCommand: Commandable, Equatable {
let side: CGFloat
let condition: String
func isEqual(to: CustomEquatable) -> Bool {
guard let rhs = to as? RotateCommand else { return false }
return side == rhs.side && condition == rhs.condition
}
}
All you need to do now is connect your CustomEquatable protocol to Swift Equatable through generic extension:
extension Equatable where Self: CustomEquatable {
static func ==(lhs: Self, rhs: Self) -> Bool {
return lhs.isEqual(to: rhs)
}
}
It's not a perfect solution, but now, you can store your objects in a array of protocol objects and use == operator with your objects as well. For example(I simplified objects a little bit):
let move = MoveCommand(movingVector: .zero, condition: "some")
let rotate = RotateCommand(side: 0, condition: "some")
var array = [Commandable]()
array.append(move)
array.append(rotate)
let equal = (move == MoveCommand(movingVector: .zero, condition: "some"))
let unequal = (move == MoveCommand(movingVector: .zero, condition: "other"))
let unequal = (move == rotate) // can't do this, compare different types
PS. Using var on struct is not a good practice, especially for performance reasons.
I believe the problem here is that the equatable protocol has self requirements. So you can solve you problem by removing equatable protocol from your Commandable protocol and make your your structs equatable instead. This will of course limit your protocol but maybe it is a trade-off that is reasonable?

RawOptionsSetType raises error in Swift in Xcode6

According to this article from NSHipster, I wanted to use the following approach to create an enum that allows bitmask:
struct Toppings : RawOptionSetType, BooleanType {
private var value: UInt = 0
init(_ value: UInt) {
self.value = value
}
// MARK: RawOptionSetType
static func fromMask(raw: UInt) -> Toppings {
return self(raw)
}
// MARK: RawRepresentable
static func fromRaw(raw: UInt) -> Toppings? {
return self(raw)
}
func toRaw() -> UInt {
return value
}
// MARK: BooleanType
var boolValue: Bool {
return value != 0
}
// MARK: BitwiseOperationsType
static var allZeros: Toppings {
return self(0)
}
// MARK: NilLiteralConvertible
static func convertFromNilLiteral() -> Toppings {
return self(0)
}
// MARK: -
static var None: Toppings { return self(0b0000) }
static var ExtraCheese: Toppings { return self(0b0001) }
static var Pepperoni: Toppings { return self(0b0010) }
static var GreenPepper: Toppings { return self(0b0100) }
static var Pineapple: Toppings { return self(0b1000) } }
It raised four following errors:
Initializer 'init' has different argument names from those required by protocol 'RawRepresentable' ('init(rawValue:)')
Type 'Toppings' does not conform to protocol 'RawRepresentable'
Type 'Toppings' does not conform to protocol 'Equatable'
Type 'Toppings' does not conform to protocol 'NilLiteralConvertible'
I managed to satisfy Equatable error by implementing func ==(lhs: Self, rhs: Self) -> Bool. But it seems to fail to recognize this function.
Is there a solution to solve all the errors above? I prefer this solution over the toRaw() one. So I am highly interested in if there can be a fix to the code above.
To conform NilLiteralConvertible add another initializer and init your struct to its nil value:
init(nilLiteral: ()) { }
To conform RawRepresentable:
typealias RawValue = UInt
var rawValue: RawValue {
get {
return value
}
}
init(rawValue value: UInt) {
self.value = value
}
When you need to find out how to conform to a specific protocol, in Xcode, cmd+click on the protocol name.