Yan*_*san 7 functional-programming scala scala-cats
我第一次使用猫来解决代码出现的第一天,我想知道是否有可能改进.
给定update具有以下签名
的方法def update(i: Instruction): PosAndDir => PosAndDir
我想出来:
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
Run Code Online (Sandbox Code Playgroud)
并且
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
Run Code Online (Sandbox Code Playgroud)
更确切地说,问题是:
.value(使用scalaz,它是透明的)?update2用理解来提高可读性?Applicative实例Seq(我知道scalaz中没有).?State[S, A]为stack-safe的别名,StateT[Eval, S , A]这是StateT[Trampoline, S, A]scalaz术语,因此runS返回Eval[A],value即使对于很长的flatMap序列,也将在没有stackoverflow的情况下运行.使用一些额外的进口
import cats.data.{State, StateT}
import cats.MonadState
import cats.syntax.functorFilter._
import cats.instances.option._
Run Code Online (Sandbox Code Playgroud)
和一些准备工作
type Walk[x] = StateT[Option, PosAndDir, x]
val stateMonad = MonadState[Walk, PosAndDir]
import stateMonad._
Run Code Online (Sandbox Code Playgroud)
你可以让你的功能看起来像这样
def update2(i: Instruction): StateT[Option, PosAndDir, Pos] =
for (pad ? get if i == Walk) yield pad.pos
Run Code Online (Sandbox Code Playgroud)
并不是说这个解决方案在2.12中不起作用,因为这种改进,你可以使它适用于这种解决方法
implicit class FunctorWithFilter[F[_] : FunctorFilter, A](fa: F[A]) {
def withFilter(f: A ? Boolean) = fa.filter(f)
}
Run Code Online (Sandbox Code Playgroud)没有实例Seq,这个答案描述了原因.虽然alleycats项目中有一些非正统的例子.我真的不知道,如果你需要Applicative[Seq],从你的代码是相当有需要Traverse[Seq],或者如果您更换sequence同sequence_连Foldable[Seq].好消息还有Foldable[Iterable]在alleycats,这里是我试图定义的东西外形相似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)
}
Run Code Online (Sandbox Code Playgroud)并没有花太多时间,但这是我试图通过Monocle库简化一些部分:
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
Run Code Online (Sandbox Code Playgroud)