Reputation: 8281
I've asked some questions recently to find a smart and readable way to solve this more complex problem.
I need a super combinator with this signature :
def runStateCombinator[T, E, A](t : T, readerFactories: Seq[T => Reader[E, State[A, Unit]]], f : E => A) : Reader[E, A] = {
Don't worry, it's just a stateful computation (State
) that needs some deps (Reader
).
Here is my current code, any improvement are more than welcome (I'm a scalaz n00b).
def runStateCombinator[T, E, A](t : T, readerFactories: Seq[T => Reader[E, State[A, Unit]]], f : E => A) : Reader[E, A] = {
def transpose[E, A](readers : Seq[Reader[E, A]]) : Reader[E, Seq[A]] =
Reader { e: E => readers.map { r => r(e) } }
def map2[E, A, B](reader: Reader[E, A])(f: (E, A) => B) : Reader[E, B] =
Reader { e => f(e, reader(e)) }
val readers = readerFactories.map(_.apply(t))
val stateReader = Readers.transpose(readers).map(_.sequenceS_[A, Unit])
Readers.map2(stateReader)((ctx, state) => state.run(f(ctx))._1)
}
Upvotes: 0
Views: 56
Reputation: 16328
In general monad transformers could be a savior, but in such small piece they could only create lots of boilerplate, especially without kind-projector.
More specifically your transpose
is just alias for sequence
, which could need additional Unapply
in case of Reader
And your map2
could be refactored to simple applicative ap
Note also, that state.run(..)._1
already have alias exec
And I've add additional .toList
as Seq
is not represented well in scalaz
Overall my attempt to simplify is
import scalaz._
import scalaz.syntax.traverse._
import scalaz.std.list._
import syntax.monad._
import Id.Id
type ReadState[E, A, F[_]] = Reader[E, F[State[A, Unit]]]
def runStateCombinator[T, E, A](t: T, readerFactories: Seq[T => ReadState[E, A, Id]], f: E => A): Reader[E, A] = {
val readers: ReadState[E, A, List] = readerFactories.map(_ (t)).toList.sequenceU
val stateReader = readers.map(_.sequenceS_[A, Unit])
stateReader <*> Reader(ctx => _.exec(f(ctx)))
}
The monad trasformer, for example could make whole sequenceU.map(_.sequenceS_
single operation without need of explicit result types.
Another option is to use monstrous ReaderWriterState
with empty writes:
import scalaz._
import scalaz.syntax.traverse._
import scalaz.std.list._
import Id.Id
import ReaderWriterStateT._
import scalaz.std.anyVal._
type ReadState[E, A, X] = ReaderWriterState[E, Unit, A, X]
def runStateCombinator[T, E, A](t: T, readerFactories: Seq[T => ReadState[E, A, Unit]], f: E => A): Reader[E, A] = {
val readers = readerFactories.map(_ (t)).toList
val stateReader = readers.sequence_[ReadState[E, A, ?], Unit](implicitly, rwstMonad[Id, E, Unit, A])
Reader(x => stateReader.exec(x, f(x))._2)
}
Upvotes: 1