How to combine reading and state monad

I am trying to combine a monad Readerwith a monad State. Here is my code:

object StatePoc extends App {
  def startsWithState : Reader[String, State[Seq[String], Unit]] = Reader { s => State { ss => (ss.filter(_.startsWith(s)), Unit)}}
  def endsWithState : Reader[String, State[Seq[String], Unit]] = Reader { s => State { ss => (ss.filter(_.endsWith(s)), Unit)}}
  def process: Kleisli[Id.Id, String, State[Seq[String], Unit]] = {
    for {
      s <- startsWithState
      e <- endsWithState
    } yield e
  }

  val all = Seq("AB", "BA", "ABA", "ABBA")

  println(process("A")(all))
}

Unfortunately, this code does not return the expected result: List(BA, ABA, ABBA)instead List(ABA, ABBA).

+4
source share
2 answers

In your function, processyou read a state object from both of your readers, but you simply return the last state object, not a combination of the two. Something like that would unite both sand e:

  def process: Kleisli[Id.Id, String, State[Seq[String], Unit]] = {
    for {
      s <- startsWithState
      e <- endsWithState
    } yield {
      State { seq => e.run(s.run(seq)._1)}
    }
  }
+4
source

Another savior can be a monad transformer, which requires some template, especially without a kind projector:

type StringReader[a] = Reader[String, a]
type StateChoose[a, b] = StateT[StringReader, a, b]
type StringsChoose[a] = StateChoose[Seq[String], a]
type StringsTrans[m[_], b] = StateT[m, Seq[String], b]

def ST = MonadState[StateChoose, Seq[String]]
def R = MonadReader[Reader, String]
def T = MonadTrans[StringsTrans]

def transform(action: String => Seq[String] => Seq[String]): StringsChoose[Unit] = for {
  s <- T.liftMU(R.ask)
  _ <- ST.modify(action(s))
} yield ()
def startsWithState = transform( s => _.filter(_.startsWith(s)))
def endsWithState = transform(s => _.filter(_.endsWith(s)))

def process: StringsChoose[Unit] = {
  for {
    s <- startsWithState
    e <- endsWithState
  } yield e
}
val all = Seq("AB", "BA", "ABA", "ABBA")

println(process.exec(all).apply("A"))
+2
source

Source: https://habr.com/ru/post/1617916/


All Articles