Reducer Monad

Hey everyone,

I don't know if this is useful in any way, but I just found something astonishing: if you generalize reducers just a tiny little bit, they are a generalization of the state monad:

struct Reducer<State, Action, Result> { 

   let _apply : (inout State, Action) -> Result 

   func apply(...) -> Result {...}

   // functor

   func map<NewResult>(_ transform: @escaping (Result) -> NewResult)
      -> Reducer<State, Action, NewResult> {
      .init {transform(_apply($0, $1))}
   }

   // monad

   init(_ pure: Result) {_apply = {_, _ in pure}}

   func flatMap<NewResult>(_ transform: @escaping (Result)
      -> Reducer<State, Action, NewResult) -> Reducer<State, Action, NewResult> {
      .init {state, action in 
         let result = _apply(&state, action)
         let nextReducer = transform(result)
         return nextReducer._apply(&state, action)
      }
   }

}

If the next reducer is independent of the result and you return always the same type, this is one possible implementation of usual reducer composition. However, if your result type happens to form a monoid (as is the case in Composable Architecture), then you can also do this:

protocol Monoid {
   static var zero : Self {get}
   func appending(_ other: Self) -> Self
}

extension Reducer : Monoid where Result : Monoid {

   static var zero : Self {Reducer{_, _ in .zero}}
   
   func appending(_ other: Reducer) -> Reducer {
      flatMap{result1 in 
        other.map{result2 in result1.appending(result2)}
   }
   }

}

Which gives exactly the expected behavior of a MonadPlus!

I just found that result worth sharing. Maybe someone out there will have fun with it, I don't think this is useful in any way :smiley:

Shameless self promotion:

By the way, in my own framework, RedCat, I have a branch using this implementation of reducer composition, even though the "standard way" in RedCat has even neater properties.

2 Likes