Interesting! thanks.
We use a similar system for our project, except we use these functions instead of the Delta data type:
combine : Effect -> Effect -> Effect
-- combine is an interface equivalent to (::) in your example
changeStateWithEffect : (State -> (State, Effect)) -> (State, Effect) -> (State, Effect)
changeStateWithEffect f (state, effect) =
let (newState, otherEffect) = f state
in (newState, combine effect otherEffect)
changeState : (State -> State) -> (State, Effect) -> (State, Effect)
changeState f (state, effect) = (f state, effect)
addEffect : (State -> Effect) -> (State, Effect) -> (State, Effect)
addEffect f (state, effect) = (state, combine effect (f state))
an example of use close to your example would be :
myStateAndEffects
|> changeState heal
|> addEffect healingParticles
|> changeStateWithEffect victoryCheck
Some benefits of this are that 1) we can not access the effects with our delta functions, 2) we are able to describe pretty easily what kind of changes the delta functions imply, and 3) for multiple effects, we can describe them using natural composition or piping style, rather than having a list handler in our system.
DeltaNone simply becomes identity.