#swift
#swift
Вопрос:
Не позволяет ли Swift накладывать ограничения связанного типа на дженерики.
typealias Reducer = (State, Action) -> State
class RootReducer {
var reducers: [Reducer] = [Reducer]()
func addReducer<T: Reducer>(_ reducer: T) {
self.reducers.append(reducer)
}
}
Я получаю следующее:
Обновить:
Вместо использования имени в виде строки, как я могу передать тип и сохранить тип в качестве ключа к словарю.
typealias Reducer = (State, Action) -> State
class RootReducer {
var reducers: [String :Reducer] = [:]
func addReducer(name: String, reducer: @escaping Reducer) {
reducers[name] = reducer
}
}
Комментарии:
1. Ну, это именно то, что указано в ошибке.
Reducer
Это не протокол и не класс. Вы не можете создать подкласс типа замыкания или функции.2. Чего вы на самом деле пытаетесь достичь, используя дженерики?
3. Reducer — это псевдоним типа. Итак, в основном я говорю, что когда вы передаете reducer в качестве аргумента, он должен принадлежать редуктору typealias.
4. @johndoe вам не обязательно использовать для этого дженерики.
5. Что вы подразумеваете под использованием типа в качестве ключа? Какой тип? Очевидно, вы каким-то образом пытаетесь реализовать Redux в Swift. Обратите внимание, что в Redux редуктор не является типом, потому что каждый редуктор принимает разные параметры и дает разные результаты. Единственное, что вы знаете о редукторе, это то, что это функция с двумя параметрами.
Ответ №1:
Технически это не ответ на ваш конкретный вопрос, но в любом случае это должно помочь. Я чувствую, что вы боретесь с концепцией типа, и из-за этого ваш вопрос нелегко понять. typealias
на самом деле ничего особенного, это просто псевдоним для типа. Это не дает вам никакой дополнительной функциональности, просто более читаемый код.
В Swift нет динамических объектов такого же типа, как в Javascript, и вы не можете напрямую переписать из него функциональность. Например, в Swift нет ничего похожего на Object.values
.
—
Ваш код, очевидно, вдохновлен Redux, поэтому давайте посмотрим, как все работает в Redux и как преобразовать его в Swift.
Давайте начнем с Action
.
protocol Action {
var type: String { get }
}
struct ActionImpl<PayloadType>: Action {
let type: String
let payload: PayloadType
}
Это всего лишь простая идея, но она должна работать в большинстве случаев использования. Обратите внимание, что в Redux действие — это в основном объект, который может содержать что угодно. Мы могли бы использовать payload: Any
в Swift, но это было бы некрасиво.
Теперь давайте определим состояние. Состояние не является словарем. Состояние является сложным иерархическим типом, например:
struct AppState1: Equatable {
var value1: Int = 0 // default value
var value2: String?
}
struct AppState2: Equatable {
var value3: Double?
}
struct RootState: Equatable {
var appState1 = AppState1()
var appState2 = AppState2()
}
Теперь, при таком состоянии, функции редуктора должны иметь следующие типы:
(AppState1, Action) -> AppState1
(AppState2, Action) -> AppState2
и корневой редуктор:
(RootState, Action) -> State
Как вы можете видеть, это не единственный тип, который можно обобщить с помощью typealias
.
Это значительно проще реализовать в Javascript, потому что Javascript не делает большой разницы между объектом и словарем, а также между строкой и ключом. А также все функции в Javascript имеют один и тот же тип…
Тем не менее, в Swift мы можем создавать типобезопасные редукторы аналогичным образом:
let appState1Reducer: (AppState1, Action) -> AppState1 = { state, action in
switch action {
case let action as ActionImpl<Int> where action.type == "changeValue1":
var newState = state
newState.value1 = action.payload
return newState
case let action as ActionImpl<String> where action.type == "changeValue2":
var newState = state
newState.value2 = action.payload
return newState
default:
return state
}
}
let appState2Reducer: (AppState2, Action) -> AppState2 = { state, action in
switch action {
case let action as ActionImpl<Double> where action.type == "changeValue3":
var newState = state
newState.value3 = action.payload
return newState
default:
return state
}
}
let rootReducer: (RootState, Action) -> RootState = { state, action in
var newState = state
newState.appState1 = appState1Reducer(state.appState1, action)
newState.appState2 = appState2Reducer(state.appState2, action)
return newState
}
let state = RootState()
print(state)
let action = ActionImpl(type: "changeValue2", payload: "New value!")
let newState = rootReducer(state, action)
print(newState)
Корневой редуктор не намного сложнее, чем composeReducers
помощник в Redux, и это все еще функция, какой она должна быть.