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?
Related
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 }
}
I am a bit stuck trying to define a container for my ui elements.
As I wanted something that encapsulates a non unique label, a value that can be any comparable object and a concept of being the preferred option I came up with the following protocol:
protocol OptionProtocol:Comparable {
associatedtype Key:Comparable
associatedtype Value:Comparable
var key:Key { get set }
var value:Value { get set }
var main:Bool { get set }
static func <(lhs: Self, rhs: Self) -> Bool
static func ==(lhs: Self, rhs: Self) -> Bool
}
extension OptionProtocol {
static func <(lhs: Self, rhs: Self) -> Bool {
let equalKeys = lhs.key == rhs.key
return equalKeys ? lhs.value < rhs.value : lhs.key < rhs.key
}
static func ==(lhs: Self, rhs: Self) -> Bool{
return (lhs.value == rhs.value) && (lhs.key == rhs.key)
}
}
Now I want to implement the protocol in a generic struct and I cant figure out how. What I want to do is
struct Option<Key, Value>: OptionProtocol {
var key:Key
var value:Value
var main:Bool
}
But the compiler complains that Type 'Option<Key, Value>' does not conform to protocol 'OptionProtocol'
Any pointer would be helpful
The answer was pretty simple. I needed to constraint Key and Value in the struct.
The following struct compiles as expected
struct Option<Key, Value>:OptionProtocol where Key:Comparable, Value:Comparable {
var key:Key
var value:Value
var main:Bool
}
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.
Node is a generic type.
struct Node<T: Hashable>: Hashable {
var label: T
init(_ label: T) {
self.label = label
}
var hashValue : Int {
get {
return label.hashValue
}
}
}
extension Node : Equatable {}
// MARK: Equatable
func ==<T>(lhs: Node<T>, rhs: Node<T>) -> Bool {
return lhs.label == rhs.label
}
But it doesn't work when I try the following:
let nodes = Set<Node<String>>()
The compiler complains that Node<String> doesn't conform to Hashable. How to make Node<String> conform to Hashable?
You have to implement the == method as part of the Equatable protocol for your struct as well:
func ==<T, K>(lhs:Node<T>, rhs:Node<K>) -> Bool {
return lhs.hashValue == rhs.hashValue
}
The reason for that is Hashable inherits from Equatable.
The following is a complete working playground example:
struct Node<T: Hashable> : Hashable {
var label: T
init(_ label: T) {
self.label = label
}
var hashValue : Int {
get {
return label.hashValue
}
}
}
func ==<T>(lhs:Node<T>, rhs:Node<T>) -> Bool {
return lhs.hashValue == rhs.hashValue
}
var nodes = Set<Node<String>>()
nodes.insert(Node("hi"))
nodes.insert(Node("ho"))
nodes.insert(Node("hi"))
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.