How to use cats and the state of Monad

I first used cats to solve day 1 of the appearance of the code, and I wonder if the situation can be improved.

Given the update method with the following signature def update(i: Instruction): PosAndDir => PosAndDir

I figured it out:

 val state: State[PosAndDir, List[Unit]] = instructions.map(i => State.modify(update(i))).toList.sequenceU val finalState = state.runS(PosAndDir(Pos(0, 0), North)).value 

And

  def update2(i: Instruction): State[PosAndDir, Option[Pos]] = State.modify(update(i)).inspect(pad => if (i == Walk) Some(pad.pos) else None) … val state = instructions.map(update2).toList.sequenceU val positions = state.runA(PosAndDir(Pos(0, 0), North)).value.flatten 

More precisely, the questions:

  • why do we need to call .value (with a tale, it's transparent)?
  • Is there a way to write update2 with a concept to improve readability?
  • Is there an Applicative instance for Seq in cats (I know there isn’t in scalal).?
  • any idea to improve the code?
+5
source share
1 answer
  • cats defines State[S, A] as an alias for the safe stack StateT[Eval, S , A] , which StateT[Trampoline, S, A] in terms of scalaz, so runS returns Eval[A] , where value will be executed without stackoverflow even very long flatMap .
  • Using additional additional resources

     import cats.data.{State, StateT} import cats.MonadState import cats.syntax.functorFilter._ import cats.instances.option._ 

    and some drugs

     type Walk[x] = StateT[Option, PosAndDir, x] val stateMonad = MonadState[Walk, PosAndDir] import stateMonad._ 

    you can make your function this way

     def update2(i: Instruction): StateT[Option, PosAndDir, Pos] = for (pad ← get if i == Walk) yield pad.pos 

    not that this solution will not work in 2.12 because of this improvement , you can make it work with this workaround

     implicit class FunctorWithFilter[F[_] : FunctorFilter, A](fa: F[A]) { def withFilter(f: A β‡’ Boolean) = fa.filter(f) } 
  • There are no examples for Seq , this answer . Although there are some non-orthodox instances in the alleycats project. I'm not sure if you need Applicative[Seq] from your code, which you need more for Traverse[Seq] , or if you replace sequence with sequence_ even Foldable[Seq] . Good News: Foldable[Iterable] in alleycats , and here is my attempt to define something external for the instance Seq

     implicit val seqInstance = new MonadFilter[Seq] with Traverse[Seq] { def traverse[G[_] : Applicative, A, B](fa: Seq[A])(f: (A) β‡’ G[B]): G[Seq[B]] = fa match { case head +: tail β‡’ f(head).map2(traverse(tail)(f))(_ +: _) case _empty β‡’ Seq.empty[B].pure[G] } def foldLeft[A, B](fa: Seq[A], b: B)(f: (B, A) β‡’ B): B = fa.foldLeft(b)(f) def foldRight[A, B](fa: Seq[A], lb: Eval[B])(f: (A, Eval[B]) β‡’ Eval[B]): Eval[B] = fa match { case head +: tail β‡’ f(head, foldRight(tail, lb)(f)) case _empty β‡’ lb } def pure[A](x: A): Seq[A] = Seq(x) def empty[A]: Seq[A] = Seq.empty[A] def flatMap[A, B](fa: Seq[A])(f: (A) β‡’ Seq[B]): Seq[B] = fa.flatMap(f) def tailRecM[A, B](a: A)(f: (A) β‡’ Seq[Either[A, B]]): Seq[B] = { @tailrec def go(seq: Seq[Either[A, B]]): Seq[B] = if (seq.contains((_: Either[A, B]).isLeft)) go(seq.flatMap { case Left(a) β‡’ f(a) case b β‡’ Seq(b) }) else seq.collect { case Right(b) β‡’ b } go(Seq(Left(a))) } override def mapFilter[A, B](fa: Seq[A])(f: (A) β‡’ Option[B]): Seq[B] = fa.flatMap(f(_).toSeq) } 
  • didn't spend a lot of time, but here is my attempt to simplify some parts with the Monocle library :

     import cats.{MonadState, Foldable, Functor} import cats.instances.option._ import cats.syntax.foldable._ import cats.syntax.functor._ import cats.syntax.functorFilter._ import monocle.macros.Lenses @Lenses case class Pos(x: Int, y: Int) sealed abstract class Dir(val cmd: Pos β‡’ Pos) case object South extends Dir(Pos.y.modify(_ - 1)) case object North extends Dir(Pos.y.modify(_ + 1)) case object East extends Dir(Pos.x.modify(_ + 1)) case object West extends Dir(Pos.x.modify(_ - 1)) @Lenses case class PosAndDir(pos: Pos, dir: Dir) val clockwise = Vector(North, East, South, West) val right: Map[Dir, Dir] = clockwise.zip(clockwise.tail :+ clockwise.head).toMap val left: Map[Dir, Dir] = right.map(_.swap) sealed abstract class Instruction(val cmd: PosAndDir β‡’ PosAndDir) case object TurnLeft extends Instruction(PosAndDir.dir.modify(left)) case object TurnRight extends Instruction(PosAndDir.dir.modify(right)) case object Walk extends Instruction(pd β‡’ PosAndDir.pos.modify(pd.dir.cmd)(pd)) def runInstructions[F[_] : Foldable : Functor](instructions: F[Instruction])(start: PosAndDir): PosAndDir = instructions.map(i => State.modify(i.cmd)).sequence_.runS(start).value 
+5
source

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


All Articles