I've been trying to implement some ways to have some kind of generic way to handle actions that would update a store.
Here's a short reproducible example. I couldn't get my head around making this with protocols ( if it's even possible) so I thought about doing this using Generic structs instead.
Here you have an Updater that can take any kind of Subject and Modifier that would modify the subject and two implementations of the apply method for concrete pair of types.
struct Updater<Subject, Modifier> {
let subject: Subject
let modifier: Modifier
init(_ subject: Subject, _ modifier: Modifier) {
self.subject = subject
self.modifier = modifier
}
}
extension Updater where Subject == State, Modifier == Add {
func apply() -> Subject {
State(value: subject.value + modifier.value)
}
}
extension Updater where Subject == State, Modifier == Remove {
func apply() -> Subject {
State(value: subject.value - modifier.value)
}
}
struct State {
let value: Int
}
struct Add {
let value: Int
}
struct Remove {
let value: Int
}
var state = State(value: 0)
print(state)
state = Updater(state, Add(value: 1)).apply()
print(state)
state = Updater(state, Remove(value: 1)).apply()
print(state)
So this works but it's not really convenient to use.
But the following doesn't work.
func apply_action<S, M>(_ state: S, _ modifier: M) -> S {
Updater(state, modifier).apply()
}
app = apply_action(app, Add(value: 1))
What's really strange is that while the snippet above isn't able to find the proper
apply
method being confused between the 2 declared methods...
The following snippet clearly show that the type of Updater is properly inferred to Updater<State, Add>
. So I'm a bit confused as to why it's confused picking the function apply from the other concrete
implementation. It clearly shouldn't be confused.
What's interesting is that I can implement the methods as such in the State without using generics but just reusing the code like this:
extension State {
func apply(_ modifier: Remove) -> State {
Updater(state, modifier).apply()
}
func apply(_ modifier: Add) -> State {
Updater(state, modifier).apply()
}
}
And this would allow writing code like this:
state = state.apply(Remove(value: 1))
But making that method generic doesn't seem possible for some reasons.