Skip to content

Commit

Permalink
Merge pull request #4582 from jozic/functor.alter
Browse files Browse the repository at this point in the history
  • Loading branch information
johnynek authored May 1, 2024
2 parents f8e04d8 + a2931f5 commit fa61d34
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 11 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, identity[A1]))

/**
* 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
3 changes: 3 additions & 0 deletions laws/src/main/scala/cats/laws/FunctorLaws.scala
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ trait FunctorLaws[F[_]] extends InvariantLaws[F] {
def covariantIdentity[A](fa: F[A]): IsEq[F[A]] =
fa.map(identity) <-> fa

def mapOrKeepToMapEquivalence[A, A1 >: A](fa: F[A], pf: PartialFunction[A, A1]): IsEq[F[A1]] =
fa.mapOrKeep(pf) <-> fa.map(a => pf.applyOrElse(a, identity[A1]))

def covariantComposition[A, B, C](fa: F[A], f: A => B, g: B => C): IsEq[F[C]] =
fa.map(f).map(g) <-> fa.map(f.andThen(g))
}
Expand Down
3 changes: 2 additions & 1 deletion laws/src/main/scala/cats/laws/discipline/FunctorTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ trait FunctorTests[F[_]] extends InvariantTests[F] {
name = "functor",
parent = Some(invariant[A, B, C]),
"covariant identity" -> forAll(laws.covariantIdentity[A] _),
"covariant composition" -> forAll(laws.covariantComposition[A, B, C] _)
"covariant composition" -> forAll(laws.covariantComposition[A, B, C] _),
"mapOrKeepToMapEquivalence" -> forAll(laws.mapOrKeepToMapEquivalence[A, A] _)
)
}

Expand Down

0 comments on commit fa61d34

Please sign in to comment.