Как создать классы, соответствующие протоколам с вложенными PAT?

Я пытаюсь использовать стирание типов для создания протокола Repository, которому можно соответствовать (аналогично AnyCollection Swift). Этот протокол необходимо обернуть в класс со стертым типом, поскольку он содержит PAT.

Однако, поскольку этот протокол имеет вложенный протокол, который также имеет PAT, это несколько усложняет ситуацию.

  • Keyable - это то, что предоставляет ключ (который в конечном итоге я хотел бы убедиться, что он также является Hashable... по одному за раз).
  • Repository - мой обобщенный "контейнерный" протокол
  • AnyKeyable обертка со стертым шрифтом для Keyable
  • AnyRepository обертка со стертым шрифтом для Repository

У меня есть фрагмент игровой площадки, который ПОЧТИ компилируется:

protocol Keyable {
    associatedtype KeyType// where KeyType: Hashable
    func key() -> KeyType
}

protocol Repository {
    associatedtype DataType: Keyable
    func all() -> [DataType]
    func get(id: DataType.KeyType) throws -> DataType?
    func create(object: DataType) throws -> Bool
    func update(object: DataType) throws -> Bool
    func delete(object: DataType) throws -> Bool
    func clear() -> Bool
}

final class AnyKeyable<T>: Keyable {
    private let _key: () -> T
    init<U: Keyable>(_ keyable: U) where U.KeyType == T {
        _key = keyable.key
    }

    public func key() -> T {
        return _key()
    }
}

final class AnyRepository<T: Keyable>: Repository {
    private let _all: () -> [T]
    private let _get: (_ id: T.KeyType) throws -> T?
    private let _create: (_ object: T) throws -> Bool
    private let _update: (_ object: T) throws -> Bool
    private let _delete: (_ object: T) throws -> Bool
    private let _clear: () -> Bool

    init<U: Repository>(_ repository: U) where U.DataType == T {
        _get = repository.get
        _create = repository.create
        _delete = repository.delete
        _update = repository.update
        _clear = repository.clear
        _all = repository.all
    }

    func all() -> [T] {
        return _all()
    }

    func get<K: Keyable>(id: K.KeyType) throws -> T? where T.KeyType: Keyable, T.KeyType == K.KeyType {
        let anyKeyable = AnyKeyable(id)
        return try _get(anyKeyable)
    }

    func create(object: T) throws -> Bool {
        return try _create(object)
    }

    func update(object: T) throws -> Bool {
        return try _update(object)
    }

    func delete(object: T) throws -> Bool {
        return try _delete(object)
    }

    func clear() -> Bool {
        return _clear()
    }
}

final class Contact {
    var name: String = ""
    var email: String = ""
}

extension Contact: Keyable {
    public typealias KeyType = String
    public func key() -> String {
        return "im the key"
    }
}

// Just a dummy class to see 
final class ContactRepository: Repository {
    typealias DataType = Contact
    private var someContacts: [Contact] = []

    func all() -> [Contact] {
        return someContacts
    }

    func clear() -> Bool {
        someContacts.removeAll()
        return someContacts.count == 0
    }

    func get(id: Contact.KeyType) throws -> Contact? {
        return nil
    }

    func update(object: Contact) throws -> Bool {
        return false
    }

    func create(object: Contact) throws -> Bool {
        return false
    }

    func delete(object: Contact) throws -> Bool {
        return false
    }
}

// Testing
let i = AnyRepository<Contact>(ContactRepository())
i.all()
i.clear()

Проблема в том, что компилятор Swift жалуется, что я не использую K в сигнатуре метода get<K: Keyable>(id: K.KeyType)... но мне кажется, что я определенно использую.

Я думаю, что компилятор жалуется из-за объявления DataType.KeyType get() в протоколе, а не в конкретном подклассе AnyRepository, но я не уверен, как это исправить, чтобы предоставить больше контекста для компилятора.

Есть ли лучший способ структурировать это, чтобы позволить мне выполнить этот шаблон? Как насчет того, чтобы позволить первому associatedtype в Keyable стать associatedtype KeyType where KeyType: Hashable?

Любая помощь приветствуется, спасибо!


person Nick DiZazzo    schedule 13.12.2017    source источник
comment
Почему вы вводите новый заполнитель для get(id:) в ластик типа? Ты точно хочешь func get(id: T.KeyType) throws -> T? { return try _get(id) }?   -  person Hamish    schedule 13.12.2017
comment
Ага - вы абсолютно правы. Этот заполнитель остался после попытки заставить его работать с материалом Hashable, который был закомментирован в моем посте. Я никогда не удалял его, и это значительно усложняло вещи больше, чем они должны были быть.   -  person Nick DiZazzo    schedule 14.12.2017


Ответы (1)


Как указал Хэмиш, не было необходимости вводить еще один общий, K. Это все усложняло. Очистив все это, теперь это работает без усложнения класса AnyKeyable:

public protocol Keyable {
    associatedtype KeyType where KeyType: Hashable
    func key() -> KeyType
}

public protocol Repository {
    associatedtype DataType: Keyable
    func all() -> [DataType]
    func get(id: DataType.KeyType) throws -> DataType?
    func create(object: DataType) throws
    func create(objects: [DataType]) throws
    func update(object: DataType) throws
    func delete(object: DataType) throws
    func delete(objects: [DataType]) throws
    func clear()
}

public class AnyRepository<T: Keyable>: Repository {
    private let _all: () -> [T]
    private let _get: (_ id: T.KeyType) throws -> T?
    private let _create: (_ object: T) throws -> Void
    private let _createAll: (_ objects: [T]) throws -> Void
    private let _update: (_ object: T) throws -> Void
    private let _delete: (_ object: T) throws -> Void
    private let _deleteAll: (_ objects: [T]) throws -> Void
    private let _clear: () -> Void

    public init<R: Repository>(_ repository: R) where R.DataType == T {
        _get = repository.get
        _create = repository.create
        _createAll = repository.create
        _delete = repository.delete
        _deleteAll = repository.delete
        _update = repository.update
        _clear = repository.clear
        _all = repository.all
    }

    public func all() -> [T] {
        return _all()
    }

    public func get(id: T.KeyType) throws -> T? {
        return try _get(id)
    }

    public func create(object: T) throws {
        return try _create(object)
    }

    public func create(objects: [T]) throws {
        return try _createAll(objects)
    }

    public func update(object: T) throws {
        return try _update(object)
    }

    public func delete(object: T) throws {
        return try _delete(object)
    }

    public func delete(objects: [T]) throws {
        return try _deleteAll(objects)
    }

    public func clear() {
        return _clear()
    }
}
person Nick DiZazzo    schedule 14.12.2017