Skip to content

Commit

Permalink
Added mapAccumL and mapAccumM operations
Browse files Browse the repository at this point in the history
  • Loading branch information
Odomontois committed Feb 16, 2021
1 parent d1f6e53 commit ab41164
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 29 deletions.
70 changes: 41 additions & 29 deletions core/src/main/scala/cats/Traverse.scala
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,22 @@ import scala.annotation.implicitNotFound

override def unorderedSequence[G[_]: CommutativeApplicative, A](fga: F[G[A]]): G[F[A]] =
sequence(fga)

/**
* a combination of map and scanLeft
* it applies a function to each element of a structure,
* passing an accumulating parameter from left to right,
* and returning a final value of this accumulator together with the new structure.
*/
def mapAccumL[A, B, C](fa: F[A], start: B)(step: (B, A) => (B, C)): (B, F[C]) =
traverse(fa)(a => State((b: B) => step(b, a))).run(start).value

/**
* like [[mapAccumL]] but also combines monadic effects of `G`
* stack-safety relies on a stack safety of G
*/
def mapAccumF[G[_]: Monad, A, B, C](fa: F[A], start: B)(step: (B, A) => G[(B, C)]): G[(B, F[C])] =
traverse(fa)(a => StateT((b: B) => step(b, a))).run(start)
}

object Traverse {
Expand All @@ -166,48 +182,38 @@ object Traverse {
object ops {
implicit def toAllTraverseOps[F[_], A](target: F[A])(implicit tc: Traverse[F]): AllOps[F, A] {
type TypeClassType = Traverse[F]
} =
new AllOps[F, A] {
type TypeClassType = Traverse[F]
val self: F[A] = target
val typeClassInstance: TypeClassType = tc
}
} = new AllOps[F, A] {
type TypeClassType = Traverse[F]
val self: F[A] = target
val typeClassInstance: TypeClassType = tc
}
}
trait Ops[F[_], A] extends Serializable {
type TypeClassType <: Traverse[F]
def self: F[A]
val typeClassInstance: TypeClassType
def traverse[G[_], B](f: A => G[B])(implicit ev$1: Applicative[G]): G[F[B]] =
typeClassInstance.traverse[G, A, B](self)(f)
def traverseTap[G[_], B](f: A => G[B])(implicit ev$1: Applicative[G]): G[F[A]] =
typeClassInstance.traverseTap[G, A, B](self)(f)
def flatTraverse[G[_], B](f: A => G[F[B]])(implicit G: Applicative[G], F: FlatMap[F]): G[F[B]] =
typeClassInstance.flatTraverse[G, A, B](self)(f)(G, F)
def sequence[G[_], B](implicit ev$1: A <:< G[B], ev$2: Applicative[G]): G[F[B]] =
typeClassInstance.sequence[G, B](self.asInstanceOf[F[G[B]]])
def flatSequence[G[_], B](implicit ev$1: A <:< G[F[B]], G: Applicative[G], F: FlatMap[F]): G[F[B]] =
typeClassInstance.flatSequence[G, B](self.asInstanceOf[F[G[F[B]]]])(G, F)
def traverse[G[_], B](f: A => G[B])(implicit ev$1: Applicative[G]): G[F[B]] = typeClassInstance.traverse[G, A, B](self)(f)
def traverseTap[G[_], B](f: A => G[B])(implicit ev$1: Applicative[G]): G[F[A]] = typeClassInstance.traverseTap[G, A, B](self)(f)
def flatTraverse[G[_], B](f: A => G[F[B]])(implicit G: Applicative[G], F: FlatMap[F]): G[F[B]] = typeClassInstance.flatTraverse[G, A, B](self)(f)(G, F)
def sequence[G[_], B](implicit ev$1: A <:< G[B], ev$2: Applicative[G]): G[F[B]] = typeClassInstance.sequence[G, B](self.asInstanceOf[F[G[B]]])
def flatSequence[G[_], B](implicit ev$1: A <:< G[F[B]], G: Applicative[G], F: FlatMap[F]): G[F[B]] = typeClassInstance.flatSequence[G, B](self.asInstanceOf[F[G[F[B]]]])(G, F)
def mapWithIndex[B](f: (A, Int) => B): F[B] = typeClassInstance.mapWithIndex[A, B](self)(f)
def traverseWithIndexM[G[_], B](f: (A, Int) => G[B])(implicit G: Monad[G]): G[F[B]] =
typeClassInstance.traverseWithIndexM[G, A, B](self)(f)(G)
def traverseWithIndexM[G[_], B](f: (A, Int) => G[B])(implicit G: Monad[G]): G[F[B]] = typeClassInstance.traverseWithIndexM[G, A, B](self)(f)(G)
def zipWithIndex: F[(A, Int)] = typeClassInstance.zipWithIndex[A](self)
def mapAccumL[B, C](start: B)(step: (B, A) => (B, C)): (B, F[C]) = typeClassInstance.mapAccumL[A, B, C](self, start)(step)
def mapAccumF[G[_], B, C](start: B)(step: (B, A) => G[(B, C)])(implicit ev$1: Monad[G]): G[(B, F[C])] = typeClassInstance.mapAccumF[G, A, B, C](self, start)(step)
}
trait AllOps[F[_], A]
extends Ops[F, A]
with Functor.AllOps[F, A]
with Foldable.AllOps[F, A]
with UnorderedTraverse.AllOps[F, A] {
trait AllOps[F[_], A] extends Ops[F, A] with Functor.AllOps[F, A] with Foldable.AllOps[F, A] with UnorderedTraverse.AllOps[F, A] {
type TypeClassType <: Traverse[F]
}
trait ToTraverseOps extends Serializable {
implicit def toTraverseOps[F[_], A](target: F[A])(implicit tc: Traverse[F]): Ops[F, A] {
type TypeClassType = Traverse[F]
} =
new Ops[F, A] {
type TypeClassType = Traverse[F]
val self: F[A] = target
val typeClassInstance: TypeClassType = tc
}
} = new Ops[F, A] {
type TypeClassType = Traverse[F]
val self: F[A] = target
val typeClassInstance: TypeClassType = tc
}
}
@deprecated("Use cats.syntax object imports", "2.2.0")
object nonInheritedOps extends ToTraverseOps
Expand All @@ -216,4 +222,10 @@ object Traverse {
/* END OF SIMULACRUM-MANAGED CODE */
/* ======================================================================== */







}
12 changes: 12 additions & 0 deletions tests/src/test/scala/cats/tests/TraverseSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,18 @@ abstract class TraverseSuite[F[_]: Traverse](name: String)(implicit ArbFInt: Arb
}
}

test(s"Traverse[$name].mapAccumL") {
forAll { (fa: F[Int], start: Long, fn: (Long, Int) => (Long, Int)) =>
val (act, fb) = fa.mapAccumL(start)(fn)
val (exp, xs) = fa.toList.foldLeft((start, List.empty[Int])) { case ((prev, acc), a) =>
val (next, b) = fn(prev, a)
(next, b :: acc)
}
assert(act === exp)
assert(fb.toList === xs.reverse)
}
}

}

object TraverseSuite {
Expand Down

0 comments on commit ab41164

Please sign in to comment.