Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add PartialFunction instance for Profunctor typeclass #3392

1 change: 1 addition & 0 deletions core/src/main/scala-2.12/cats/instances/all.scala
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ trait AllInstances
with TupleInstances
with UUIDInstances
with VectorInstances
with PartialFunctionInstances

trait AllInstancesBinCompat0 extends FunctionInstancesBinCompat0 with Tuple2InstancesBinCompat0

Expand Down
1 change: 1 addition & 0 deletions core/src/main/scala-2.12/cats/instances/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package object instances {
object float extends FloatInstances
object finiteDuration extends CoreFiniteDurationInstances with FiniteDurationInstances
object function extends FunctionInstances with FunctionInstancesBinCompat0
object partialFunction extends PartialFunctionInstances
object future extends FutureInstances
object int extends IntInstances
object invariant extends InvariantMonoidalInstances
Expand Down
1 change: 1 addition & 0 deletions core/src/main/scala-2.13+/cats/instances/all.scala
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ trait AllInstances
with TupleInstances
with UUIDInstances
with VectorInstances
with PartialFunctionInstances

trait AllInstancesBinCompat0 extends FunctionInstancesBinCompat0 with Tuple2InstancesBinCompat0

Expand Down
1 change: 1 addition & 0 deletions core/src/main/scala-2.13+/cats/instances/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package object instances {
object float extends FloatInstances
object finiteDuration extends CoreFiniteDurationInstances with FiniteDurationInstances
object function extends FunctionInstances with FunctionInstancesBinCompat0
object partialFunction extends PartialFunctionInstances
object future extends FutureInstances
object int extends IntInstances
object invariant extends InvariantMonoidalInstances
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/Applicative.scala
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ object Applicative {
* scala> import cats.Applicative.catsApplicativeForArrow
* scala> val toLong: Int => Long = _.toLong
* scala> val double: Int => Int = 2*_
* scala> val f: Int => (Long, Int) = catsApplicativeForArrow.product(toLong, double)
* scala> val f: Int => (Long, Int) = catsApplicativeForArrow[Function1, Int].product(toLong, double)
gagandeepkalra marked this conversation as resolved.
Show resolved Hide resolved
* scala> f(3)
* res0: (Long, Int) = (3,6)
* }}}
Expand Down
3 changes: 3 additions & 0 deletions core/src/main/scala/cats/arrow/Compose.scala
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ object Compose {
cats.instances.function.catsStdInstancesForFunction1
implicit def catsComposeForMap: Compose[Map] = cats.instances.map.catsStdComposeForMap

implicit def catsInstancesForPartialFunction: ArrowChoice[PartialFunction] with CommutativeArrow[PartialFunction] =
cats.instances.partialFunction.catsStdInstancesForPartialFunction

/****************************************************************************/
/* THE FOLLOWING CODE IS MANAGED BY SIMULACRUM; PLEASE DO NOT EDIT!!!! */
/****************************************************************************/
Expand Down
3 changes: 3 additions & 0 deletions core/src/main/scala/cats/arrow/Profunctor.scala
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ object Profunctor {
implicit def catsStrongForFunction1: Strong[Function1] =
cats.instances.function.catsStdInstancesForFunction1

implicit def catsStrongForPartialFunction: Strong[PartialFunction] =
cats.instances.partialFunction.catsStdInstancesForPartialFunction

/****************************************************************************/
/* THE FOLLOWING CODE IS MANAGED BY SIMULACRUM; PLEASE DO NOT EDIT!!!! */
/****************************************************************************/
Expand Down
9 changes: 4 additions & 5 deletions core/src/main/scala/cats/instances/function.scala
Original file line number Diff line number Diff line change
Expand Up @@ -136,11 +136,10 @@ sealed private[instances] trait Function1Instances extends Function1Instances0 {

implicit val catsStdInstancesForFunction1: ArrowChoice[Function1] with CommutativeArrow[Function1] =
new ArrowChoice[Function1] with CommutativeArrow[Function1] {
def choose[A, B, C, D](f: A => C)(g: B => D): Either[A, B] => Either[C, D] =
_ match {
case Left(a) => Left(f(a))
case Right(b) => Right(g(b))
}
def choose[A, B, C, D](f: A => C)(g: B => D): Either[A, B] => Either[C, D] = {
case Left(a) => Left(f(a))
case Right(b) => Right(g(b))
}

def lift[A, B](f: A => B): A => B = f

Expand Down
64 changes: 64 additions & 0 deletions core/src/main/scala/cats/instances/partialFunction.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package cats.instances
import cats.arrow.{ArrowChoice, CommutativeArrow}

trait PartialFunctionInstances {

implicit def catsStdInstancesForPartialFunction: ArrowChoice[PartialFunction] with CommutativeArrow[PartialFunction] =
PartialFunctionInstances.instance
}

private object PartialFunctionInstances {

private val instance: ArrowChoice[PartialFunction] with CommutativeArrow[PartialFunction] =
new ArrowChoice[PartialFunction] with CommutativeArrow[PartialFunction] {

/**
* {{{
* scala> import cats.arrow.Arrow
* scala> import cats.syntax.arrowChoice._
* scala> val toLong: PartialFunction[Int, Long] = Arrow[PartialFunction].lift(_.toLong)
* scala> val toDouble: PartialFunction[Float, Double] = Arrow[PartialFunction].lift(_.toDouble)
* scala> val f: PartialFunction[Either[Int, Float], Either[Long, Double]] = toLong +++ toDouble
* scala> f(Left(3))
* res0: Either[Long,Double] = Left(3)
* scala> f(Right(3))
* res1: Either[Long,Double] = Right(3.0)
* }}}
*/
override def choose[A, B, C, D](
f: PartialFunction[A, C]
)(g: PartialFunction[B, D]): PartialFunction[Either[A, B], Either[C, D]] = {
case Left(a) if f.isDefinedAt(a) => Left(f(a))
case Right(b) if g.isDefinedAt(b) => Right(g(b))
}

override def lift[A, B](f: A => B): PartialFunction[A, B] = { case a if a.isInstanceOf[A] => f(a) }
gagandeepkalra marked this conversation as resolved.
Show resolved Hide resolved

/**
* Create a new `F` that takes two inputs, but only modifies the first input
*
* Example:
* {{{
* scala> import cats.arrow.Arrow
* scala> val f: PartialFunction[Int, Int] = Arrow[PartialFunction].lift(_ * 2)
* scala> val fab = Arrow[PartialFunction].first[Int,Int,Int](f)
* scala> fab((2,3))
* res0: (Int, Int) = (4,3)
* }}}
*/
override def first[A, B, C](fa: PartialFunction[A, B]): PartialFunction[(A, C), (B, C)] = {
case (a, c) if fa.isDefinedAt(a) => (fa(a), c)
}

override def split[A, B, C, D](
f: PartialFunction[A, B],
g: PartialFunction[C, D]
): PartialFunction[(A, C), (B, D)] = {
case (a, c) if f.isDefinedAt(a) && g.isDefinedAt(c) => (f(a), g(c))
}

override def compose[A, B, C](f: PartialFunction[B, C], g: PartialFunction[A, B]): PartialFunction[A, C] = {
case a if g.isDefinedAt(a) && f.isDefinedAt(g(a)) => f(g(a))
gagandeepkalra marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
6 changes: 4 additions & 2 deletions kernel/src/main/scala/cats/kernel/Eq.scala
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package cats.kernel

import java.util.UUID

import cats.kernel.compat.scalaVersionSpecific._

import scala.collection.immutable.{BitSet, Queue, SortedMap, SortedSet}
import scala.concurrent.duration.{Duration, FiniteDuration}
import scala.math.Equiv
import scala.{specialized => sp}
import scala.util.{Failure, Success, Try}
import compat.scalaVersionSpecific._
import scala.{specialized => sp}

/**
* A type class used to determine equality between 2 instances of the same
Expand Down
12 changes: 8 additions & 4 deletions laws/src/main/scala/cats/laws/discipline/Eq.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,10 @@ package cats
package laws
package discipline

import cats.data.RepresentableStore
import cats.Eq
import cats.data.AndThen
import cats.data.{AndThen, RepresentableStore}
import cats.instances.boolean._
import cats.instances.int._
import cats.instances.string._
import cats.instances.tuple._
import cats.kernel._
import cats.platform.Platform
import cats.syntax.eq._
Expand All @@ -22,6 +19,13 @@ object eq {
implicit def catsLawsEqForFn2[A, B, C](implicit ev: Eq[((A, B)) => C]): Eq[(A, B) => C] =
Eq.by((_: (A, B) => C).tupled)

implicit def catsLawsEqForPartialFunctionExhaustive[A: ExhaustiveCheck, B: Eq]: Eq[PartialFunction[A, B]] =
Eq.instance((f, g) =>
ExhaustiveCheck[A].allValues
.filter(a => f.isDefinedAt(a) || g.isDefinedAt(a))
.forall(a => f.isDefinedAt(a) && g.isDefinedAt(a) && Eq[B].eqv(f(a), g(a)))
)

implicit def catsLawsEqForAndThen[A, B](implicit eqAB: Eq[A => B]): Eq[AndThen[A, B]] =
Eq.by[AndThen[A, B], A => B](identity)

Expand Down
22 changes: 22 additions & 0 deletions tests/src/test/scala/cats/tests/PartialFunctionSuite.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package cats.tests
import cats.arrow.{ArrowChoice, CommutativeArrow}
import cats.kernel.laws.discipline.SerializableTests
import cats.laws.discipline.arbitrary._
import cats.laws.discipline.eq._
import cats.laws.discipline.{ArrowChoiceTests, CommutativeArrowTests, MiniInt}

class PartialFunctionSuite extends CatsSuite {

checkAll("ArrowChoice[PartialFunction]", SerializableTests.serializable(ArrowChoice[PartialFunction]))

checkAll("PartialFunction",
ArrowChoiceTests[PartialFunction].arrowChoice[MiniInt, MiniInt, MiniInt, MiniInt, MiniInt, MiniInt]
)

checkAll("CommutativeArrow[PartialFunction]", SerializableTests.serializable(CommutativeArrow[PartialFunction]))
checkAll(
"PartialFunction",
CommutativeArrowTests[PartialFunction].commutativeArrow[MiniInt, MiniInt, MiniInt, MiniInt, MiniInt, MiniInt]
)

}