How to create a protocol conform Comparable in Swift - swift

I have a use case to compare the enployee's rank. Here is what I want to do:
protocol Enployee: Comparable {
var id: String { get }
var rank: Int { get }
var name: String { get }
var type: String { get }
}
extension Enployee {
static func <(lhs: Enployee, rhs: Enployee) -> Bool {
return lhs.rank < rhs.rank
}
}
But I got the following error:
Protocol 'Enployee' can only be used as a generic constraint because it has Self or associated type requirements
Then I changed my code:
extension Enployee {
static func <(lhs: Self, rhs: Self) -> Bool {
return lhs.rank < rhs.rank
}
}
I can compile it. But when I continue working on my user case:
struct Engineer: Enployee {
var id: String
var rank: Int
var name: String
let type: String = "Engineer"
}
struct Manager: Enployee {
var id: String
var rank: Int
var name: String
let type: String = "Manager"
}
let staff1 = Engineer(id: "123", rank: 2, name: "Joe")
let staff2 = Engineer(id: "124", rank: 2, name: "Frank")
let staff3 = Manager(id: "101", rank: 10, name: "John")
public struct Department<T: Comparable> {
}
let queue = Department<Enployee>()
I got another error message:
Protocol 'Enployee' as a type cannot conform to 'Comparable'
Any idea?

The error message tells you what the problem is. Having declared Department<T: Comparable> you cannot resolve T as Enployee; it is a protocol, not a type conforming to Comparable (such as Engineer).
A generic has to be resolved to one type. It would be legal to declare Department<T: Enployee> if that is what you really mean. But then you are setting yourself up for multiple Department types, a department of Manager and a department of Engineer. That is probably not what you want, so this is a bad use of a generic.
Moreover, type strings are a horrifically bad smell:
struct Engineer: Enployee {
var id: String
var rank: Int
var name: String
let type: String = "Engineer" // uck
}
It seems much more likely that you want a class and subclasses here. Why fight against OOP?

Related

Swift protocol default values are not changable

I have such a protocol properties with default values. But with the current implementation if I create an instance of AssetViewAttributes with some other values for avgPrice, precision they still have the default values. How can I change them?
struct Crypto: AssetViewAttribures {
let name: String
let logo: URL
let symbol: String
let avgPrice: String
let precision: Int
}
struct Commodity: AssetViewAttribures {
let name: String
let logo: URL
let symbol: String
let avgPrice: String
let precision: Int
}
struct Fiat: AssetViewAttribures {
let name: String
let logo: URL
let symbol: String
}
protocol AssetViewAttribures {
var name: String { get }
var logo: URL { get }
var symbol: String { get }
}
extension AssetViewAttribures {
var avgPrice: String { get { return "" } set {} }
var precision: Int { get{ return 0 } set{} }
}
var type1: AssetViewAttribures = Crypto(name: "name", logo: URL(string: "https://pixabay.com/de/illustrations/online-maus-web-internet-weltweit-523234/")!, symbol: "symbol", avgPrice: "123", precision: 2)
type1.avgPrice // "" instead of "123"
var type1: AssetViewAttribures = Crypto(name: "name", logo: URL(string: "https://pixabay.com/de/illustrations/online-maus-web-internet-weltweit-523234/")!, symbol: "symbol", avgPrice: "123", precision: 2)
type1.avgPrice
This would call the getter declared in the protocol extension, which just returns "". This is because Crypto.avgPrice has no relation to the avgPrice declared in the protocol extension. You can't "override" a member in an extension, because extensions are dispatched statically. The compiler sees that test is of type AssetViewAttributes, finds the default getter you have declared in the extension, and that's what it will call.
To fix this, you need to add avgPrice as a requirement of the protocol:
protocol AssetViewAttributes {
...
var avgPrice: String { get }
}
This causes Swift to find avgPrice declared in the protocol, and dispatches it dynamically. If the implementing class happens to implement avgPrice, that implementation will be called. If not, then the default implementation is called.

Some Problems about Opaque Return Type and Protocol with associatedtype In Swift

How to deal with this problem?
Definitions:
protocol NameProtocol {
var rawValue: String { get }
}
struct CatName: NameProtocol {
enum CatNamesEnum: String {
case Tiger, Max, Sam
}
var literal: CatNamesEnum
var rawValue: String {
literal.rawValue
}
}
struct DogName: NameProtocol {
enum DogNamesEnum: String {
case Jack, Toby, Sadie
}
var literal: DogNamesEnum
var rawValue: String {
literal.rawValue
}
}
Definitions:
protocol AnimalProtocol {
associatedtype Name: NameProtocol
var name: Name { get }
func cry() -> String
}
class Cat: AnimalProtocol {
var name: CatName
func cry() -> String {
return "meow, I am \(name.rawValue)"
}
init(name: CatName) {
self.name = name
}
// some other cat actions...
}
class Dog: AnimalProtocol {
var name: DogName
func cry() -> String {
return "bark, I am \(name.rawValue)"
}
init(name: DogName) {
self.name = name
}
// some other dog actions...
}
The code above are some definition code structure, should not be modified.
And the functions below takes some problem:
Protocol with asccociatedtype cannot be the dictionary value type.
Function with Opaque Return Type cannot return some different types extends the same protocol.
// <1>
// Error: Protocol 'AnimalProtocol' can only be used as a generic constraint because it has Self or associated type requirements
let animals: [String: AnimalProtocol] = [
"cat": Cat(name: CatName(literal: .Sam)),
"dog": Dog(name: DogName(literal: .Jack))
// ...
// maybe a lot of animals
]
for (animal, entity) in animals {
print("\(animal): \(entity.cry())")
}
// <2>
// Error: Function declares an opaque return type, but the return statements in its body do not have matching underlying types
func animalCry(animal: String) -> some AnimalProtocol {
switch animal {
case "cat":
return Cat(name: CatName(literal: .Sam))
default:
return Dog(name: DogName(literal: .Toby))
}
}
Any solutions?
Or best practice of different types(with embed generic type) in a list.
You say that the definitions should not be modified but this is exactly what I have done here:
I removed the associated type from the AnimalProtocol protocol since it wasn't used elsewhere in any conforming types
protocol AnimalProtocol {
var name: NameProtocol { get }
func cry() -> String
}
Then I changed both Cat and Dog to conform to the protocol by changing the name declaration in both to
var name: NameProtocol
this resolves the issue with the dictionary and the function was fixed by changing the return type from some AnimalProtocol to AnimalProtocol

How to evaluate equality for homogeneous collections?

The case:
Consider the following:
protocol Car {
static var country: String { get }
var id: Int { get }
var name: String { get set }
}
struct BMW: Car {
static var country: String = "Germany"
var id: Int
var name: String
}
struct Toyota: Car {
static var country: String = "Japan"
var id: Int
var name: String
}
Here I have a simple example of how to create an abstraction layer by using a -Car- protocol, thus I am able to declare a heterogeneous collection of cars:
let cars: [Car] = [BMW(id: 101, name: "X6"), Toyota(id: 102, name: "Prius")]
And it works fine.
The problem:
I want to be able to evaluate the equality of the cars (by id), example:
cars[0] != cars[1] // true
So, what I tried to do is to let Car to conforms to Equatable protocol:
protocol Car: Equatable { ...
However, I got the "typical" compile-time error:
error: protocol 'Car' can only be used as a generic constraint because
it has Self or associated type requirements
I am unable to declare cars: [Car] array anymore. If I am not mistaking, the reason behind it is that Equatable uses Self so it would be considered as homogeneous.
How can I handle this problem? Could Type erasure be a mechanism to resolve it?
A possible solution is a protocol extension, instead of an operator it provides an isEqual(to function
protocol Car {
static var country: String { get }
var id: Int { get }
var name: String { get set }
func isEqual(to car : Car) -> Bool
}
extension Car {
func isEqual(to car : Car) -> Bool {
return self.id == car.id
}
}
and use it
cars[0].isEqual(to: cars[1])
Here is solution using Type Erasure:
protocol Car {
var id: Int { get }
var name: String { get set }
}
struct BMW: Car {
var id: Int
var name: String
}
struct Toyota: Car {
var id: Int
var name: String
}
struct AnyCar: Car, Equatable {
private var carBase: Car
init(_ car: Car) {
self.carBase = car
}
var id: Int { return self.carBase.id }
var name: String {
get { return carBase.name}
set { carBase.name = newValue }
}
public static func ==(lhs: AnyCar, rhs: AnyCar) -> Bool {
return lhs.carBase.id == rhs.carBase.id
}
}
let cars: [AnyCar] = [AnyCar(BMW(id: 101, name: "X6")), AnyCar(Toyota(id: 101, name: "Prius"))]
print(cars[0] == cars[1])
Don't know how to implement this with static property. If I figure out, I will edit this answer.
Some good solutions to the general problem have already been given – if you just want a way to compare two Car values for equality, then overloading == or defining your own equality method, as shown by #Vyacheslav and #vadian respectively, is a quick and simple way to go. However note that this isn't an actual conformance to Equatable, and therefore won't compose for example with conditional conformances – i.e you won't be able to then compare two [Car] values without defining another equality overload.
The more general solution to the problem, as shown by #BohdanSavych, is to build a wrapper type that provides the conformance to Equatable. This requires more boilerplate, but generally composes better.
It's worth noting that the inability to use protocols with associated types as actual types is just a current limitation of the Swift language – a limitation that will likely be lifted in future versions with generalised existentials.
However it often helps in situations like this to consider whether your data structures can be reorganised to eliminate the need for a protocol to begin with, which can eliminate the associated complexity. Rather than modelling individual manufacturers as separate types – how about modelling a manufacturer as a type, and then have a property of this type on a single Car structure?
For example:
struct Car : Hashable {
struct ID : Hashable {
let rawValue: Int
}
let id: ID
struct Manufacturer : Hashable {
var name: String
var country: String // may want to consider lifting into a "Country" type
}
let manufacturer: Manufacturer
let name: String
}
extension Car.Manufacturer {
static let bmw = Car.Manufacturer(name: "BMW", country: "Germany")
static let toyota = Car.Manufacturer(name: "Toyota", country: "Japan")
}
extension Car {
static let bmwX6 = Car(
id: ID(rawValue: 101), manufacturer: .bmw, name: "X6"
)
static let toyotaPrius = Car(
id: ID(rawValue: 102), manufacturer: .toyota, name: "Prius"
)
}
let cars: [Car] = [.bmwX6, .toyotaPrius]
print(cars[0] != cars[1]) // true
Here we're taking advantage of the automatic Hashable synthesis introduced in SE-0185 for Swift 4.1, which will consider all of Car's stored properties for equality. If you want to refine this to only consider the id, you can provide your own implementation of == and hashValue (just be sure to enforce the invariant that if x.id == y.id, then all the other properties are equal).
Given that the conformance is so easily synthesised, IMO there's no real reason to just conform to Equatable rather than Hashable in this case.
A couple of other noteworthy things in the above example:
Using a ID nested structure to represent the id property instead of a plain Int. It doesn't make sense to perform Int operations on such a value (what does it mean to subtract two identifiers?), and you don't want to be able to pass a car identifier to something that for example expects a pizza identifier. By lifting the value into its own strong nested type, we can avoid these issues (Rob Napier has a great talk that uses this exact example).
Using convenience static properties for common values. This lets us for example define the manufacturer BMW once and then re-use the value across different car models that they make.
It is possible to override ==:
import UIKit
var str = "Hello, playground"
protocol Car {
static var country: String { get }
var id: Int { get }
var name: String { get set }
}
struct BMW: Car {
static var country: String = "Germany"
var id: Int
var name: String
}
struct Toyota: Car {
static var country: String = "Japan"
var id: Int
var name: String
}
func ==(lhs: Car, rhs: Car) -> Bool {
return lhs.id == rhs.id
}
BMW(id:0, name:"bmw") == Toyota(id: 0, name: "toyota")

Protocol Composition can't be used in struct in swift

protocol is a kind of type in swift and should be used in everywhere as a type, so why Protocol Composition can't be used in struct in swift?
protocol Named {
var name: String { get }
}
protocol Aged {
var age: Int { get }
}
struct Person: Named, Aged {
var name: String
var age: Int
}
func wishHappyBirthday(to celebrator: Named & Aged) {
print("Happy birthday, \(celebrator.name), you're \(celebrator.age)!")
}
let birthdayPerson = Person(name: "Malcolm", age: 21)
wishHappyBirthday(to: birthdayPerson)
// Prints "Happy birthday, Malcolm, you're 21!"
In struct Person, if I change it into
struct Person: Named & Aged {
var name: String
var age: Int
}
the complier error appears: Protocol composition is neither allowed nor needed here
So why I can use Protocol composition in function rather than struct?

How to work around Swift not supporting first class meta types?

So I'm implementing the following:
A simple LanguageType protocol, which conforms to Hashable
A Translateable protocol, which should allow you to get (and set) a [String] from a dictionary, using a LanguageType as key
// MARK: - LanguageType
protocol LanguageType: Hashable {
var description: String { get }
}
extension LanguageType {
var description: String { return "\(Self.self)" }
var hashValue: Int { return "\(Self.self)".hashValue }
}
func ==<T: LanguageType, U: LanguageType>(left: T, right: U) -> Bool {
return left.description == right.description
}
// MARK: - Translateable
protocol Translateable {
var translations: [LanguageType: [String]] { get set }
}
As usual, Swift has a problem with the way the LanguageType protocol is used:
From what I've read, this has to do with Swift not supporting Existentials, which results in protocols not actually being first class types.
In the context of generics this problem can usually be solved with a type-erased wrapper.
In my case there are no generics or associated types though.
What I want to achieve is to have translations.Key to be any LanguageType, not just one generic type conforming to LanguageType.
So for example this wouldn't work:
protocol Translateable {
typealias Language: LanguageType
var translations: [Language: [String]] { get set }
}
For some reason I just can't think of a way to achieve this. I find it sounds like I need some kind of type-erased wrapper, as I want
translations.Key to be any LanguageType
I think I need to erase the exact type, which is supposed to conform to LanguageType in Translateable.
What can I do to fix this issue?
Update 1:
As just determined in this question, LanguageType actually has associated type requirements (do to it's conformance to Equatable). Therefore I will try to create a type-erased wrapper around LanguageType.
Update 2:
So I've realized, that creating a type-erased wrapper for LanguageType won't actually resolve the problem. I've created AnyLanguage:
struct AnyLanguage<T>: LanguageType {
private let _description: String
var description: String { return _description }
init<U: LanguageType>(_ language: U) { _description = language.description }
}
func ==<T, U>(left: AnyLanguage<T>, right: AnyLanguage<U>) -> Bool {
return left.description == right.description
}
If I now used it in place of LanguageType it wouldn't do much, as Translateable would still require an associated type:
protocol Translateable {
typealias T
var translations: [AnyLanguage<T>: [String]] { get set }
}
Solution:
I removed the generic from AnyLanguage:
struct AnyLanguage: LanguageType {
private(set) var description: String
init<T: LanguageType>(_ language: T) { description = language.description }
}
func ==(left: AnyLanguage, right: AnyLanguage) -> Bool {
return left.description == right.description
}
protocol Translateable {
var translations: [AnyLanguage: [String]] { get set }
}
Not sure why I introduced T in Update 2, as it doesn't do anything. But this seems to work now.
You can't have protocols as a key for a Dictionary, see Swift Dictionary with Protocol Type as Key. Swift need to tie the dictionary key to a concrete type.
Seems that you're trying to achieve static polymorphism and dynamic polymorphism in the same construct (the Translateable protocol), which I'm not sure it can be achieved.
A workaround would be to declare Translateable as a generic struct:
struct Translateable<T: LanguageType> {
var translations: [T: [String]]
}
Possibly you can use an enum that conforms to LanguageType can mimic the behaviour you're looking for. In that case, you needn't explicitly include conformance to hashable in LanguageType, as enums are Hashable.
protocol LanguageType {
var description: String { get }
// ...
}
extension LanguageType {
var description: String { return "\(Self.self)" }
}
enum AnyLanguage : Int, LanguageType {
case English = 1, German, Swedish
// implement non-default description
var description : String {
return "Language: " + String(self)
}
}
protocol Translatable {
var myDict : [AnyLanguage:[String]] { get set }//= [:]
}
class MyFooWordList : Translatable {
private var myBackendDict : [AnyLanguage:[String]] = [:]
var myDict : [AnyLanguage:[String]] {
get {
return myBackendDict
}
set {
for (k, v) in newValue {
myBackendDict[k] = v
}
}
}
}
Example:
/* Example */
var myFooWordList = MyFooWordList()
myFooWordList.myDict = [.English: ["Hello", "World"]]
myFooWordList.myDict = [.German: ["Hallo", "Welt"]]
print("Words for '" + AnyLanguage.English.description + "': \(myFooWordList.myDict[.English] ?? ["<Empty>"])")
/* Words for 'Language: English': ["Hello", "World"] */
print("Words for '" + AnyLanguage.German.description + "': \(myFooWordList.myDict[.German] ?? ["<Empty>"])")
/* Words for 'Language: German': ["Hallo", "Welt"] */
print("Words for '" + AnyLanguage.Swedish.description + "': \(myFooWordList.myDict[.Swedish] ?? ["<Empty>"])")
/* Words for 'Language: Swedish': ["<Empty>"] */
Another workaround is to use an enum-like class where you can "dynamically add members" to this fictive enum
class LanguageType {
class AnyLanguage: Hashable {
let id: Int
let description: String
private init(id: Int, description: String) {
self.id = id
self.description = description
}
var hashValue: Int { return id }
}
class var ENGLISH: AnyLanguage {
class English: AnyLanguage {
}
return English(id: 1, description: "English")
}
class var GERMAN: AnyLanguage {
class German: AnyLanguage {
}
return German(id: 2, description: "German")
}
class func CUSTOM(id: Int, _ description: String) -> AnyLanguage {
return AnyLanguage(id: id, description: description)
}
}
func == (lhs: LanguageType.AnyLanguage, rhs: LanguageType.AnyLanguage) -> Bool {
return lhs.id == rhs.id
}
protocol Translatable {
var myDict : [LanguageType.AnyLanguage:[String]] { get set }//= [:]
}
class MyFooWordList : Translatable {
private var myBackendDict : [LanguageType.AnyLanguage:[String]] = [:]
var myDict : [LanguageType.AnyLanguage:[String]] {
get {
return myBackendDict
}
set {
for (k, v) in newValue {
myBackendDict[k] = v
}
}
}
}
Example usage
/* Example */
var myFooWordList = MyFooWordList()
myFooWordList.myDict = [LanguageType.ENGLISH: ["Hello", "World"]]
myFooWordList.myDict = [LanguageType.GERMAN: ["Hallo", "Welt"]]
myFooWordList.myDict = [LanguageType.CUSTOM(3, "Swedish"): ["Hej", "Varlden"]]
myFooWordList.myDict = [LanguageType.CUSTOM(4, "Finnish"): ["Hei", "Maailma"]]
print("Words for '" + LanguageType.ENGLISH.description + "': \(myFooWordList.myDict[LanguageType.ENGLISH] ?? ["<Empty>"])")
/* Words for 'English': ["Hello", "World"] */
print("Words for '" + LanguageType.GERMAN.description + "': \(myFooWordList.myDict[LanguageType.GERMAN] ?? ["<Empty>"])")
/* Words for 'Language: German': ["Hallo", "Welt"] */
print("Words for '" + LanguageType.CUSTOM(3, "Swedish").description + "': \(myFooWordList.myDict[LanguageType.CUSTOM(3, "Swedish")] ?? ["<Empty>"])")
/* Words for 'Swedish': ["Hej", "Varlden"] */
The solution seems to be a type-erased wrapper. Type-erasure fixes the problem of not being able to use protocols with associated types (PATs) as first-class citizens, by creating a wrapper type, which only exposes the properties defined by the protocol, which it wraps.
In this case, LanguageType is a PAT, due to its adoption of Equatable (which it conforms to, due to its adoption of Hashable):
protocol LanguageType: Hashable { /*...*/ }
Therefore it can not be used as a first-class type in the Translatable protocol:
protocol Translatable {
var translations: [LanguageType: [String]] { get set } // error
}
Defining an associated type for Translatable would not fix the problem, as this would constrain the LanguageType to be one specific type:
protocol Translatable {
typealias Language: LanguageType
var translations: [Language: [String]] { get set } // works
}
struct MyTranslatable<T: LanguageType>: Translatable {
var translations: [T: [String]] // `T` can only be one specific type
//...
}
As mentioned the solution is a type-erased wrapper AnyLanguage (Apple uses the same naming convention for their type-erased wrappers. For example AnySequence):
// `AnyLanguage` exposes all of the properties defined by `LanguageType`
// in this case, there's only the `description` property
struct AnyLanguage: LanguageType {
private(set) var description: String
// `AnyLanguage` can be initialized with any type conforming to `LanguageType`
init<T: LanguageType>(_ language: T) { description = language.description }
}
// needed for `AnyLanguage` to conform to `LanguageType`, as the protocol inherits for `Hashable`, which inherits from `Equatable`
func ==(left: AnyLanguage, right: AnyLanguage) -> Bool {
return left.description == right.description
}
// the use of `AnyLanguage` allows any `LanguageType` to be used as the dictionary's `Key`, as long as it is wrapped as `AnyLanguage`
protocol Translateable {
var translations: [AnyLanguage: [String]] { get set }
}
This implementation now allows the following:
struct SomethingTranslatable: Translatable {
var translations: [AnyLanguage: [String]] = [:]
}
func ==(left: SomethingTranslatable, right: SomethingTranslatable) -> Bool { /*return some `Bool`*/ }
struct English: LanguageType { }
struct German: LanguageType { }
var something = SomethingTranslatable()
something.translations[AnyLanguage(English())] = ["Hello", "World"]
let germanWords = something.translations[AnyLanguage(German())]
Different types, conforming to LanguageType, can now be used as the Key. The only syntactical difference, is the necessary initialization of an AnyLanguage:
AnyLanguage(English())