Yeah, double dispatch is what I'm trying to achieve but so far without much luck to make it generic. Here's an example:
struct State {
let value: Int
}
struct OtherState {
let value: Int
}
struct SuperState {
let state1: State
let state2: OtherState
}
struct Add {
let value: Int
func dispatch(_ state: SuperState) -> SuperState {
SuperState(
state1: self.dispatch(state.state1),
state2: self.dispatch(state.state2)
)
}
func dispatch(_ subject: State) -> State {
State(
value: subject.value + value
)
}
func dispatch(_ subject: OtherState) -> OtherState {
OtherState(
value: subject.value + value
)
}
}
struct Sub {
let value: Int
func dispatch(_ state: SuperState) -> SuperState {
SuperState(
state1: self.dispatch(state.state1),
state2: self.dispatch(state.state2)
)
}
func dispatch(_ subject: State) -> State {
State(
value: subject.value - value
)
}
func dispatch(_ subject: OtherState) -> OtherState {
OtherState(
value: subject.value - value
)
}
}
extension SuperState {
func dispatch(_ modifier: Add) -> SuperState {
modifier.dispatch(self)
}
func dispatch(_ modifier: Sub) -> SuperState {
modifier.dispatch(self)
}
}
extension State {
func dispatch(_ modifier: Add) -> State {
modifier.dispatch(self)
}
func dispatch(_ modifier: Sub) -> State {
modifier.dispatch(self)
}
}
var state = SuperState(
state1: State(value: 0),
state2: OtherState(value: 0)
)
state = state.dispatch(Add(value: 1))
print(state)
state = state.dispatch(Sub(value: 1))
print(state)
state = state.dispatch(Add(value: 1))
print(state)
Here, there is no generic/protocol and it works fine, but it forces you to write a lot of boiler plate like the dispatch method on the state that is basically just
modifier.dispatch(self)
Where the modifier is a different type and thanks to method overloading in swift it's fairly easy to implement with some boiler plate code.
You'd be tempted to write protocol like this to save some typing. Given the constraint of generic, we can't use generic functions in the protocol because it would still fail when trying to return either a State or SuperState... because swift doesn't generate specialized functions in a way it's useful here.
For example you could have the following protocol:
protocol Dispatchable {
func dispatch<T: Dispatch>(_ state: T) -> T
}
protocol Dispatch {
func dispatch<T: Dispatchable>(_ modifier: T) -> Self
}
extension Dispatch {
func dispatch<T: Dispatchable>(_ modifier: T) -> Self {
print("default dispatch called")
return modifier.dispatch(self)
}
}
extension Dispatchable {
func dispatch<T: Dispatch>(_ state: T) -> T {
print("default dispatchable called")
return state
}
}
And an extension like this for the Sub
modifier.
extension Sub: Dispatchable {
func dispatch<T: Dispatch>(_ state: T) -> T {
if let state = state as? SuperState {
return self.dispatch(state) as! T
} else if let state = state as? State {
return self.dispatch(state) as! T
} else if let state = state as? OtherState {
return self.dispatch(state) as! T
} else {
return state
}
}
func dispatch(_ state: SuperState) -> SuperState {
SuperState(
state1: state.state1.dispatch(self),
state2: state.state2.dispatch(self)
)
}
func dispatch(_ state: State) -> State {
State(value: state.value + value)
}
func dispatch(_ state: OtherState) -> OtherState {
OtherState(value: state.value + value)
}
}
This works but there's no real way to get rid of dynamic dispatch with the generic method and that also mean you just can't add new types without overriding the generic dispatch method.
I could get this to work using only static dispatch but the protocol declaration look like this:
protocol Dispatchable {
}
protocol Dispatch {
}
extension Dispatchable {
func dispatch(_ state: SuperState) -> SuperState {
return state
}
func dispatch(_ state: State) -> State {
return state
}
func dispatch(_ state: OtherState) -> OtherState {
return state
}
}
extension Dispatch {
func dispatch(_ modifier: Add) -> SuperState {
modifier.dispatch(self as! SuperState)
}
func dispatch(_ modifier: Sub) -> SuperState {
modifier.dispatch(self as! SuperState)
}
func dispatch(_ modifier: Add) -> State {
modifier.dispatch(self as! State)
}
func dispatch(_ modifier: Sub) -> State {
modifier.dispatch(self as! State)
}
func dispatch(_ modifier: Add) -> OtherState {
modifier.dispatch(self as! OtherState)
}
func dispatch(_ modifier: Sub) -> OtherState {
modifier.dispatch(self as! OtherState)
}
}
You can see how adding a new modifier or state type would force creating a lot more function on the protocol than needed.
I'm a bit lost here.
Static dispatch: SwiftFiddle - Swift Online Playground
Dynamic Dispatch: SwiftFiddle - Swift Online Playground
If I could get the generic thing to automatically call the specialized function it would be great.