Skip to content

Commit

Permalink
Add alter to Functor
Browse files Browse the repository at this point in the history
  • Loading branch information
jozic committed Apr 4, 2024
1 parent 67aad39 commit 362080c
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 0 deletions.
13 changes: 13 additions & 0 deletions core/src/main/scala/cats/Functor.scala
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,18 @@ trait Functor[F[_]] extends Invariant[F] { self =>
*/
def tupleRight[A, B](fa: F[A], b: B): F[(A, B)] = map(fa)(a => (a, b))

/**
* Modifies the `A` value in `F[A]` with the supplied function, if the function is defined for the value.
* Example:
* {{{
* scala> import cats.Functor
* scala> import cats.implicits.catsStdInstancesForList
* scala> Functor[List].alter(List(1, 2, 3)) { case 2 => 42 }
* res0: List[Int] = List(1, 42, 3)
* }}}
*/
def alter[A](fa: F[A])(pf: PartialFunction[A,A]): F[A] = map(fa)(a => if (pf.isDefinedAt(a)) pf(a) else a)

/**
* Un-zips an `F[(A, B)]` consisting of element pairs or Tuple2 into two separate F's tupled.
*
Expand Down Expand Up @@ -258,6 +270,7 @@ object Functor {
def as[B](b: B): F[B] = typeClassInstance.as[A, B](self, b)
def tupleLeft[B](b: B): F[(B, A)] = typeClassInstance.tupleLeft[A, B](self, b)
def tupleRight[B](b: B): F[(A, B)] = typeClassInstance.tupleRight[A, B](self, b)
def alter(pf: PartialFunction[A, A]): F[A] = typeClassInstance.alter(self)(pf)
}
trait AllOps[F[_], A] extends Ops[F, A] with Invariant.AllOps[F, A] {
type TypeClassType <: Functor[F]
Expand Down
8 changes: 8 additions & 0 deletions tests/shared/src/test/scala/cats/tests/FunctorSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ class FunctorSuite extends CatsSuite {
}
}

test("alter maps matching elements preserving structure") {
forAll { (l: List[Int], o: Option[Int], m: Map[String, Int]) =>
assert(l.alter { case i if i % 2 == 0 => i + 1 } === l.map(i => if (i % 2 == 0) i + 1 else i))
assert(o.alter { case i if i > 0 => i + 1 } === (if (o.nonEmpty) if (o.get > 0) Some(o.get + 1) else o else None))
assert(m.alter { case v if v % 2 == 0 => v + 1 } === m.map { case (k, v) => k -> (if (v % 2 == 0) v + 1 else v) })
}
}

test("tupleLeft and tupleRight tuple values with a constant value preserving structure") {
forAll { (l: List[Int], o: Option[Int], m: Map[String, Int], i: Int) =>
assert(l.tupleLeft(i) === (List.tabulate(l.length)(in => (i, l(in)))))
Expand Down

0 comments on commit 362080c

Please sign in to comment.