How to work with Swift without supporting first class meta types?

So, I implement the following:

  • A simple protocol LanguageTypethat matchesHashable
  • A Translateable, which should allow you to get (and install) a [String]from the dictionary using the LanguageTypeas 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 how the protocol is used LanguageType:

Mistake

From what I read, this is due to the fact that Swift does not support Existentials , which leads to the fact that the protocols are not actually first class types.

In the context of generics, this problem can usually be solved with an erasable erasable wrap.
In my case, there are no generics or related types.

translations.Key LanguageType, , LanguageType.
, , :

protocol Translateable {
    typealias Language: LanguageType

    var translations: [Language: [String]] { get set }
}

- . , , - ,

translations.Key LanguageType

, , LanguageType Translateable. , ?


1: , LanguageType ( Equatable). LanguageType.

2: , LanguageType . 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
}

LanguageType, , Translateable :

protocol Translateable {
    typealias T
    var translations: [AnyLanguage<T>: [String]] { get set }
}

:

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

, T Update 2, . .

+4
3

- . - (PAT) , , , .

LanguageType PAT, - Equatable ( , - Hashable):

protocol LanguageType: Hashable { /*...*/ }

Translatable:

protocol Translatable {
    var translations: [LanguageType: [String]] { get set } // error
}

Translatable , LanguageType :

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

    //...
}

, AnyLanguage (Apple , . AnySequence):

// `AnyLanguage` exposes all of the properties defined by `LanguageType`
// in this case, there 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 `Key`, as long as it is wrapped as `AnyLanguage`
protocol Translateable {
    var translations: [AnyLanguage: [String]] { get set }
}

:

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())]

Key , LanguageType. - AnyLanguage:

AnyLanguage(English())
0

Dictionary, . Swift Dictionary . .

, ( Translateable), , .

Translateable :

struct Translateable<T: LanguageType> {
    var translations: [T: [String]]
}
+3

, enum, LanguageType, , . hashable LanguageType, 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 */
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>"] */

enum -like, " "

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 */
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"] */
+1
source

Source: https://habr.com/ru/post/1623535/


All Articles