Skip to content

Commit

Permalink
Add mapOrKeep to Functor
Browse files Browse the repository at this point in the history
  • Loading branch information
jozic committed Apr 5, 2024
1 parent 67aad39 commit 0c12d73
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 10 deletions.
14 changes: 14 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,19 @@ 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].mapOrKeep(List(1, 2, 3)) { case 2 => 42 }
* res0: List[Int] = List(1, 42, 3)
* }}}
*/
def mapOrKeep[A, A1 >: A](fa: F[A])(pf: PartialFunction[A, A1]): F[A1] = map(fa)(a => pf.applyOrElse(a, _ => 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 +271,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 mapOrKeep[A1 >: A](pf: PartialFunction[A, A1]): F[A1] = typeClassInstance.mapOrKeep[A, A1](self)(pf)
}
trait AllOps[F[_], A] extends Ops[F, A] with Invariant.AllOps[F, A] {
type TypeClassType <: Functor[F]
Expand Down
21 changes: 11 additions & 10 deletions docs/nomenclature.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,17 @@ _WARNING_: this page is written manually, and not automatically generated, so ma

### Functor

| Type | Method Name |
| ------------- |--------------|
| `F[A] => F[Unit]` | `void` |
| `F[A] => B => F[B]` | `as` |
| `F[A] => (A => B) => F[B]` | `map` |
| `F[A] => (A => B) => F[(A,B)]` | `fproduct` |
| `F[A] => (A => B) => F[(B,A)]` | `fproductLeft` |
| `F[A] => B => F[(B, A)]` | `tupleLeft` |
| `F[A] => B => F[(A, B)]` | `tupleRight` |
| `(A => B) => (F[A] => F[B])` | `lift` |
| Type | Method Name | Notes |
|--------------------------------|----------------|-------|
| `F[A] => F[Unit]` | `void` |
| `F[A] => B => F[B]` | `as` |
| `F[A] => (A => B) => F[B]` | `map` |
| `F[A] => (A => A1) => F[A1])` | `mapOrKeep` | A1 >: A, the (A => A1) is a PartialFunction
| `F[A] => (A => B) => F[(A,B)]` | `fproduct` |
| `F[A] => (A => B) => F[(B,A)]` | `fproductLeft` |
| `F[A] => B => F[(B, A)]` | `tupleLeft` |
| `F[A] => B => F[(A, B)]` | `tupleRight` |
| `(A => B) => (F[A] => F[B])` | `lift` |

### Apply

Expand Down
12 changes: 12 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,18 @@ class FunctorSuite extends CatsSuite {
}
}

test("mapOrKeep maps matching elements preserving structure") {
forAll { (l: List[Int], o: Option[Int], m: Map[String, Int]) =>
assert(l.mapOrKeep { case i if i % 2 == 0 => i + 1 } === l.map(i => if (i % 2 == 0) i + 1 else i))
assert(
o.mapOrKeep { case i if i > 0 => i + 1 } === (if (o.nonEmpty) if (o.get > 0) Some(o.get + 1) else o else None)
)
assert(
m.mapOrKeep { 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 0c12d73

Please sign in to comment.