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 Decidable #2607

Open
wants to merge 91 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
91 commits
Select commit Hold shift + click to select a range
289c2fb
Add Decideable
stephen-lazaro Nov 5, 2018
ab2105f
Add first batch of instances
stephen-lazaro Nov 5, 2018
b290417
Another batch of instances
stephen-lazaro Nov 7, 2018
8ebe564
Tuple2K instance
stephen-lazaro Nov 7, 2018
4ae9ba0
IdT instance
stephen-lazaro Nov 7, 2018
6ceae06
Draft law
stephen-lazaro Nov 8, 2018
764b8ca
Some instance fixes...
stephen-lazaro Nov 10, 2018
04b1433
Scale back those insane laws
stephen-lazaro Nov 10, 2018
57abb07
Initial tests...
stephen-lazaro Nov 10, 2018
ad4c062
First pass at docs
stephen-lazaro Nov 10, 2018
244e7a5
Retuned laws with mostly reasonable right distributivity
stephen-lazaro Nov 10, 2018
a8f8079
Comment out dead code warning
stephen-lazaro Nov 10, 2018
164bc81
Hmmmm
stephen-lazaro Nov 12, 2018
a14d7c4
Fix implicit conflict
stephen-lazaro Jul 2, 2019
0a6fbf5
Fix style and so forth
stephen-lazaro Jul 2, 2019
b310dba
Merge remote-tracking branch 'typelevel/master' into slazaro/decideable
stephen-lazaro Jul 2, 2019
5b5f04b
Fix everything but MIMA
stephen-lazaro Jul 2, 2019
79f96a8
Merge branch 'master' into slazaro/decideable
stephen-lazaro Jul 21, 2020
45fe0c4
Format and fix OptionT instance
stephen-lazaro Jul 21, 2020
f3ce9ec
More lintings
stephen-lazaro Jul 21, 2020
2d38c82
Fix build settings
stephen-lazaro Jul 21, 2020
26d812d
Undo weird thing
stephen-lazaro Jul 21, 2020
ce3f380
Double down
stephen-lazaro Jul 21, 2020
289588a
Try and get better cross-version compat
stephen-lazaro Jul 21, 2020
f9d8f18
And more so
stephen-lazaro Jul 21, 2020
ec6920b
Fix some version specific instances
stephen-lazaro Jul 21, 2020
4af7db2
Pretty weird stuff I guess
stephen-lazaro Jul 21, 2020
8a99300
Formatting
stephen-lazaro Jul 21, 2020
a27b391
Another linting
stephen-lazaro Jul 21, 2020
39e6395
Fix tut probably and remove Const instance
stephen-lazaro Jul 21, 2020
d94ad94
Some clean up, breaks tests
stephen-lazaro Jul 22, 2020
7e357aa
Missed a bit
stephen-lazaro Jul 22, 2020
12ef1f9
Significantly improved tests
stephen-lazaro Jul 22, 2020
af15b3c
Formatter
stephen-lazaro Jul 22, 2020
ea48c08
Improve some instances, drop the product sum law
stephen-lazaro Jul 28, 2020
a0325e8
Fix laws references
stephen-lazaro Jul 28, 2020
c8ff7b8
Add back law and weaken some instances
stephen-lazaro Jul 28, 2020
55bdd91
Fix a test
stephen-lazaro Jul 28, 2020
692c3e0
Clean flags
stephen-lazaro Jul 28, 2020
85d29ef
Clean some imports
stephen-lazaro Jul 28, 2020
beb03d6
Improve binary compat
stephen-lazaro Jul 28, 2020
dccb090
Try to fix some mima issues
stephen-lazaro Jul 28, 2020
d88f178
Baffling that that works
stephen-lazaro Jul 28, 2020
d67ee5c
Formatting
stephen-lazaro Jul 29, 2020
2ebeb45
Merge branch 'main' into slazaro/decideable
stephen-lazaro Jun 26, 2022
b64f3f8
Fix merge
stephen-lazaro Jun 26, 2022
45966c3
Formatting
stephen-lazaro Jun 26, 2022
91962c4
Heaaders for rights
stephen-lazaro Jun 27, 2022
72289b1
Clean docs and suchlike
stephen-lazaro Jun 27, 2022
87fe236
No implicitNotFound
stephen-lazaro Jun 27, 2022
3d96d06
Accidental newline
stephen-lazaro Jun 27, 2022
8ab26f1
s/INothing/Nothing/g
stephen-lazaro Jun 27, 2022
9edb1e4
Make some things vals, re-organize methods
stephen-lazaro Jun 28, 2022
ffe0459
Avoid binary incompat
stephen-lazaro Jun 28, 2022
8bcaa2e
Make alias final
stephen-lazaro Jun 28, 2022
da8224a
Another val
stephen-lazaro Jun 28, 2022
b051a4d
More vals, some docs cleaning
stephen-lazaro Jun 29, 2022
c787ae8
Remove Haskell style name aliases
stephen-lazaro Jun 29, 2022
13570c7
Another val
stephen-lazaro Jun 29, 2022
7cc84b7
Equiv `val`
stephen-lazaro Jun 29, 2022
fef5ff0
Correct for managed code no longer managed
stephen-lazaro Jun 29, 2022
011c8eb
Use simpler expression in Composed
stephen-lazaro Jun 29, 2022
7066dbd
Adjust initialization, clean a couple tests
stephen-lazaro Jun 30, 2022
cb51073
Fix inter-doc link
stephen-lazaro Jul 1, 2022
92dfde7
Fix JS linking in tests
stephen-lazaro Jul 1, 2022
2844752
Add a couple MIMA exceptions
stephen-lazaro Jul 1, 2022
0e3ebe8
Actually fix bincompat, not exclude
stephen-lazaro Jul 2, 2022
3a0b2c4
Fix the formatting
stephen-lazaro Jul 2, 2022
7730deb
Update CONTRIBUTING with `prePR`
stephen-lazaro Jul 2, 2022
68c27f2
Commit suggested doc comment change
stephen-lazaro Jul 6, 2022
4814ed5
Commit link to original source change
stephen-lazaro Jul 6, 2022
3daa7e1
Clean and modernize syntax
stephen-lazaro Jul 6, 2022
587f274
Fix syntax modernization and add deprecations on old instances
stephen-lazaro Jul 6, 2022
73033a1
s/ReaderT/Kleisli/
stephen-lazaro Jul 6, 2022
6e75fba
Undo some instance re-arranging that is no longer relevant
stephen-lazaro Jul 6, 2022
ebf2acf
Eliminate unnecessary names match
stephen-lazaro Jul 6, 2022
bf5e352
Simpler bincompat strategy for Eq and Equiv
stephen-lazaro Jul 6, 2022
8d61434
Fix docsite a bit
stephen-lazaro Jul 6, 2022
bcf4801
Remove some odd instance scoping in tests
stephen-lazaro Jul 6, 2022
5274449
Remove companion object from docsite
stephen-lazaro Jul 6, 2022
e06a780
Define test Predicate instance via std instance
stephen-lazaro Jul 6, 2022
0938926
Law for `decide` consistency
stephen-lazaro Jul 12, 2022
9620658
Add a law for zero
stephen-lazaro Jul 12, 2022
019ebf7
A couple identity laws
stephen-lazaro Jul 12, 2022
a9e56b9
Simplify zero identity laws
stephen-lazaro Jul 12, 2022
a60cadd
Possibly rigged versions of the `Nothing` tests
stephen-lazaro Jul 12, 2022
00a6cd9
Re-structure Nothing based constraints in tests
stephen-lazaro Jul 21, 2022
3ef22ed
Comment on weird instance, shrug shoulders
stephen-lazaro Jul 21, 2022
0c9c090
Delete right absorption
stephen-lazaro Aug 18, 2022
70f42df
Corrected Eq
stephen-lazaro Aug 18, 2022
d5be88a
Merge branch 'main' into slazaro/decideable
stephen-lazaro Oct 30, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,8 @@ To build Cats you should have
* `console`: launch a REPL
* `test`: run the tests
* `unidoc`: generate the documentation
* `fmt`: run formatting of the code
* `validate`: run tests, style-checker, and doc generation
* `scalafmtAll`: run formatting of the code
* `prePR`: run tests, style-checker, and doc generation

#### Scala and Scala.js

Expand Down
6 changes: 6 additions & 0 deletions core/src/main/scala/cats/Applicative.scala
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,12 @@ trait Applicative[F[_]] extends Apply[F] with InvariantMonoidal[F] { self =>
val G = ContravariantMonoidal[G]
}

def composeDecidable[G[_]: Decidable]: Decidable[λ[α => F[G[α]]]] =
new ComposedApplicativeDecidable[F, G] {
val F = self
val G = Decidable[G]
}

/**
* Returns the given argument (mapped to Unit) if `cond` is `false`,
* otherwise, unit lifted into F.
Expand Down
12 changes: 12 additions & 0 deletions core/src/main/scala/cats/Composed.scala
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,18 @@ private[cats] trait ComposedContravariantCovariant[F[_], G[_]] extends Contravar
F.contramap(fga)(gb => G.map(gb)(f))
}

private[cats] trait ComposedApplicativeDecidable[F[_], G[_]]
stephen-lazaro marked this conversation as resolved.
Show resolved Hide resolved
extends Decidable[λ[α => F[G[α]]]]
with ComposedApplicativeContravariantMonoidal[F, G] { outer =>
def F: Applicative[F]
def G: Decidable[G]

def sum[A, B](fa: F[G[A]], fb: F[G[B]]): F[G[Either[A, B]]] =
F.map2(fa, fb)(G.sum)

override lazy val zero: F[G[Nothing]] = F.pure(G.zero)
}

private[cats] trait ComposedApplicativeContravariantMonoidal[F[_], G[_]]
extends ContravariantMonoidal[λ[α => F[G[α]]]] { outer =>
def F: Applicative[F]
Expand Down
1 change: 0 additions & 1 deletion core/src/main/scala/cats/ContravariantMonoidal.scala
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ trait ContravariantMonoidal[F[_]] extends ContravariantSemigroupal[F] with Invar
* the diagonal
*/
def trivial[A]: F[A] = contramap(unit)(_ => ())

}
object ContravariantMonoidal extends SemigroupalArityFunctions {
def monoid[F[_], A](implicit f: ContravariantMonoidal[F]): Monoid[F[A]] =
Expand Down
73 changes: 73 additions & 0 deletions core/src/main/scala/cats/Decidable.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Copyright (c) 2015 Typelevel
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

package cats

/**
* [[Decidable]] functors are functors that supply
* a `decide` operation allowing choices to be made.
*
* This is comparable to [[Alternative]] in the
* covariant case.
*
* Must obey laws in `cats.laws.DecidableLaws`.
*
* Based on ekmett's contravariant library:
* [[https://hackage.haskell.org/package/contravariant-1.4/docs/Data-Functor-Contravariant-Divisible.html#g:2]]
*/
trait Decidable[F[_]] extends ContravariantMonoidal[F] {
def sum[A, B](fa: F[A], fb: F[B]): F[Either[A, B]]
def zero: F[Nothing]

def decide[A, B, C](fa: F[A], fb: F[B])(f: C => Either[A, B]): F[C] =
contramap(sum(fa, fb))(f)

def lose[A](f: A => Nothing): F[A] = contramap[Nothing, A](zero)(f)
}
Comment on lines +36 to +44
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok I realize this is a bit late in the day, but wouldn't it make more sense to define it like this? Basically trade two abstract defs (contramap+sum) for one (decide).

// abstract
def decide[A, B, C](fa: F[A], fb: F[B])(f: C => Either[A, B]): F[C]

// concrete
def contramap[A, B](fa: F[A])(f: B => A): F[B] =
  decide(fa, zero)(b => Left(f(b)))
def sum[A, B](fa: F[A], fb: F[B]): F[Either[A, B]] =
  decide(fa, fb)(identity)

Copy link
Member

@armanbilge armanbilge Oct 12, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on principle of power, I suspect that this similar adjustment should also be made.

// abstract
def lose[A](f: A => Nothing): F[A]

// concrete
def zero: F[Nothing] = lose(identity)

Copy link
Contributor Author

@stephen-lazaro stephen-lazaro Oct 28, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine with this! I think this actually better fits our current modeling with a monoidal / semigroupal hierarchy. This encoding I think predates some of the rest of that work and was originally part of that push.
EDIT: Remembering now, I think actually we went back and forth on this and at that time sentiment went otherwise, I am flexible on this - I think either makes sense but perhaps this is more coherent with the traditional modeling of say implementing Apply. At one point, I think interest went more towards having the monoidal, comonoidal, or unit items be the fundamentals, and everything else be derived (i.e. not principle of power, but principle of algebraic composition, as it were).


object Decidable {

/**
* Summon an instance of [[Decidable]] for `F`.
*/
@inline def apply[F[_]](implicit instance: Decidable[F]): Decidable[F] = instance

trait Ops[F[_], A] extends Serializable {
type TypeClassType <: Decidable[F]
def self: F[A]
val typeClassInstance: TypeClassType
def sum[B](fb: F[B]): F[Either[A, B]] = typeClassInstance.sum[A, B](self, fb)
def decide[B, C](fb: F[B])(f: C => Either[A, B]): F[C] = typeClassInstance.decide[A, B, C](self, fb)(f)
}
trait AllOps[F[_], A] extends Ops[F, A] with ContravariantMonoidal.AllOps[F, A] {
type TypeClassType <: Decidable[F]
}
trait ToDecidableOps extends Serializable {
implicit def toDecidableOps[F[_], A](target: F[A])(implicit tc: Decidable[F]): Ops[F, A] {
type TypeClassType = Decidable[F]
} =
new Ops[F, A] {
type TypeClassType = Decidable[F]
val self: F[A] = target
val typeClassInstance: TypeClassType = tc
}
}
}
14 changes: 12 additions & 2 deletions core/src/main/scala/cats/Invariant.scala
Original file line number Diff line number Diff line change
Expand Up @@ -176,9 +176,12 @@ object Invariant extends ScalaVersionSpecificInvariantInstances with InvariantIn
cats.instances.ordering.catsContravariantMonoidalForOrdering
implicit def catsContravariantMonoidalForPartialOrdering: ContravariantMonoidal[PartialOrdering] =
cats.instances.partialOrdering.catsContravariantMonoidalForPartialOrdering
implicit def catsContravariantMonoidalForEq: ContravariantMonoidal[Eq] =
stephen-lazaro marked this conversation as resolved.
Show resolved Hide resolved

@deprecated("Prefer cats.instances.eq.catsDecidableForEq, which supersedes this instance", "2.9.0")
def catsContravariantMonoidalForEq: ContravariantMonoidal[Eq] =
cats.instances.eq.catsContravariantMonoidalForEq
implicit def catsContravariantMonoidalForEquiv: ContravariantMonoidal[Equiv] =
@deprecated("Prefer cats.instances.eq.catsDecidableForEquiv, which supersedes this instance", "2.9.0")
def catsContravariantMonoidalForEquiv: ContravariantMonoidal[Equiv] =
cats.instances.equiv.catsContravariantMonoidalForEquiv
implicit def catsContravariantForHash: Contravariant[Hash] =
cats.instances.all.catsContravariantForHash
Expand All @@ -195,6 +198,13 @@ object Invariant extends ScalaVersionSpecificInvariantInstances with InvariantIn
implicit def catsInvariantForFractional: Invariant[Fractional] =
cats.instances.invariant.catsInvariantForFractional

implicit def catsDecidableForEquiv: Decidable[Equiv] =
cats.instances.equiv.catsDecidableForEquiv
implicit def catsDecidableForEq: Decidable[Eq] =
cats.instances.eq.catsDecidableForEq
implicit def catsDecidableForPredicate: Decidable[* => Boolean] =
cats.instances.function.catsStdDecidableForPredicate

implicit val catsInvariantMonoid: Invariant[Monoid] = new Invariant[Monoid] {

def imap[A, B](fa: Monoid[A])(f: A => B)(g: B => A): Monoid[B] =
Expand Down
6 changes: 4 additions & 2 deletions core/src/main/scala/cats/Semigroupal.scala
Original file line number Diff line number Diff line change
Expand Up @@ -108,15 +108,17 @@ object Semigroupal extends ScalaVersionSpecificSemigroupalInstances with Semigro
cats.instances.ordering.catsContravariantMonoidalForOrdering
implicit val catsSemigroupalForPartialOrdering: Semigroupal[PartialOrdering] =
cats.instances.partialOrdering.catsContravariantMonoidalForPartialOrdering
implicit val catsSemigroupalForEq: Semigroupal[Eq] = cats.instances.eq.catsContravariantMonoidalForEq
implicit val catsSemigroupalForEq: Semigroupal[Eq] = cats.instances.eq.catsDecidableForEq
implicit val catsSemigroupalForEquiv: Semigroupal[Equiv] =
cats.instances.equiv.catsContravariantMonoidalForEquiv
cats.instances.equiv.catsDecidableForEquiv
implicit val catsSemigroupalForMonoid: Semigroupal[Monoid] =
cats.instances.invariant.catsSemigroupalForMonoid
implicit val catsSemigroupalForSemigroup: Semigroupal[Semigroup] =
cats.instances.invariant.catsInvariantMonoidalSemigroup
implicit val catsSemigroupalForCommutativeSemigroup: Semigroupal[CommutativeSemigroup] =
cats.instances.invariant.catsInvariantMonoidalCommutativeSemigroup
implicit val catsSemigroupalForPredicate: Semigroupal[* => Boolean] =
cats.instances.function.catsStdDecidableForPredicate

/**
* Summon an instance of [[Semigroupal]] for `F`.
Expand Down
1 change: 0 additions & 1 deletion core/src/main/scala/cats/data/Const.scala
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,6 @@ sealed abstract private[data] class ConstInstances extends ConstInstances0 {
}

sealed abstract private[data] class ConstInstances0 extends ConstInstances1 {

implicit def catsDataContravariantMonoidalForConst[D: Monoid]: ContravariantMonoidal[Const[D, *]] =
new ContravariantMonoidal[Const[D, *]] {
override def unit = Const.empty[D, Unit]
Expand Down
14 changes: 14 additions & 0 deletions core/src/main/scala/cats/data/IdT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,15 @@ sealed private[data] trait IdTApplicative[F[_]] extends Applicative[IdT[F, *]] w
def pure[A](a: A): IdT[F, A] = IdT.pure(a)
}

sealed private[data] trait IdTDecidable[F[_]] extends Decidable[IdT[F, *]] with IdTContravariantMonoidal[F] {
implicit val F0: Decidable[F]

override def sum[A, B](fa: IdT[F, A], fb: IdT[F, B]): IdT[F, Either[A, B]] =
IdT(F0.sum(fa.value, fb.value))

override lazy val zero: IdT[F, Nothing] = IdT[F, Nothing](F0.zero)
}

sealed private[data] trait IdTContravariantMonoidal[F[_]] extends ContravariantMonoidal[IdT[F, *]] {
implicit val F0: ContravariantMonoidal[F]

Expand Down Expand Up @@ -186,6 +195,11 @@ sealed abstract private[data] class IdTInstances6 extends IdTInstances7 {
sealed abstract private[data] class IdTInstances5 extends IdTInstances6 {
implicit def catsDataFunctorForIdT[F[_]](implicit F: Functor[F]): Functor[IdT[F, *]] =
new IdTFunctor[F] { implicit val F0: Functor[F] = F }

implicit def catsDataDecidableForIdT[F[_]](implicit
F: Decidable[F]
): Decidable[IdT[F, *]] =
new IdTDecidable[F] { implicit val F0: Decidable[F] = F }
}

sealed abstract private[data] class IdTInstances4 extends IdTInstances5 {
Expand Down
16 changes: 16 additions & 0 deletions core/src/main/scala/cats/data/Kleisli.scala
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,10 @@ sealed abstract private[data] class KleisliInstances0 extends KleisliInstances0_
implicit def F: Monad[F] = F0
}

implicit def catsDataDecidableForKleisli[F[_], A](implicit
F0: Decidable[F]
): Decidable[Kleisli[F, A, *]] =
new KleisliDecidable[F, A] { def F: Decidable[F] = F0 }
}

sealed abstract private[data] class KleisliInstances0_5 extends KleisliInstances1 {
Expand Down Expand Up @@ -621,6 +625,18 @@ private[data] trait KleisliAlternative[F[_], A]
implicit def F: Alternative[F]
}

sealed private[data] trait KleisliDecidable[F[_], D]
extends Decidable[Kleisli[F, D, *]]
with KleisliContravariantMonoidal[F, D] {
implicit def F: Decidable[F]

def sum[A, B](fa: Kleisli[F, D, A], fb: Kleisli[F, D, B]): Kleisli[F, D, Either[A, B]] =
Kleisli(d => F.sum(fa.run(d), fb.run(d)))

override val zero: Kleisli[F, D, Nothing] =
Kleisli[F, D, Nothing](_ => F.zero)
}

sealed private[data] trait KleisliContravariantMonoidal[F[_], D] extends ContravariantMonoidal[Kleisli[F, D, *]] {
implicit def F: ContravariantMonoidal[F]

Expand Down
29 changes: 23 additions & 6 deletions core/src/main/scala/cats/data/Nested.scala
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,6 @@ sealed abstract private[data] class NestedInstances extends NestedInstances0 {
val FG: NonEmptyTraverse[λ[α => F[G[α]]]] = NonEmptyTraverse[F].compose[G]
}

implicit def catsDataContravariantMonoidalForApplicativeForNested[F[_]: Applicative, G[_]: ContravariantMonoidal]
: ContravariantMonoidal[Nested[F, G, *]] =
new NestedContravariantMonoidal[F, G] with NestedContravariant[F, G] {
val FG: ContravariantMonoidal[λ[α => F[G[α]]]] = Applicative[F].composeContravariantMonoidal[G]
}

implicit def catsDataDeferForNested[F[_], G[_]](implicit F: Defer[F]): Defer[Nested[F, G, *]] =
new Defer[Nested[F, G, *]] {
def defer[A](fa: => Nested[F, G, A]): Nested[F, G, A] =
Expand Down Expand Up @@ -136,6 +130,12 @@ sealed abstract private[data] class NestedInstances1 extends NestedInstances2 {
val FG: Reducible[λ[α => F[G[α]]]] = Reducible[F].compose[G]
}

implicit def catsDataDecidableForApplicativeForNested[F[_]: Applicative, G[_]: Decidable]
: Decidable[Nested[F, G, *]] =
new NestedDecidable[F, G] with NestedContravariant[F, G] {
val FG: Decidable[λ[α => F[G[α]]]] = Applicative[F].composeDecidable[G]
}

implicit def catsDataFunctorForContravariantForNested[F[_]: Contravariant, G[_]: Contravariant]
: Functor[Nested[F, G, *]] =
new NestedFunctor[F, G] {
Expand All @@ -149,6 +149,12 @@ sealed abstract private[data] class NestedInstances2 extends NestedInstances3 {
val FG: Foldable[λ[α => F[G[α]]]] = Foldable[F].compose[G]
}

implicit def catsDataContravariantMonoidalForApplicativeForNested[F[_]: Applicative, G[_]: ContravariantMonoidal]
: ContravariantMonoidal[Nested[F, G, *]] =
new NestedContravariantMonoidal[F, G] with NestedContravariant[F, G] {
val FG: ContravariantMonoidal[λ[α => F[G[α]]]] = Applicative[F].composeContravariantMonoidal[G]
}

implicit def catsDataContravariantForCovariantNested[F[_]: Contravariant, G[_]: Functor]
: Contravariant[Nested[F, G, *]] =
new NestedContravariant[F, G] {
Expand Down Expand Up @@ -394,6 +400,17 @@ private[data] trait NestedContravariant[F[_], G[_]] extends Contravariant[Nested
Nested(FG.contramap(fga.value)(f))
}

private[data] trait NestedDecidable[F[_], G[_]]
extends Decidable[Nested[F, G, *]]
with NestedContravariantMonoidal[F, G] {
def FG: Decidable[λ[α => F[G[α]]]]

def sum[A, B](fa: Nested[F, G, A], fb: Nested[F, G, B]): Nested[F, G, Either[A, B]] =
Nested(FG.sum(fa.value, fb.value))

lazy val zero: Nested[F, G, Nothing] = Nested[F, G, Nothing](FG.zero)
}

private[data] trait NestedContravariantMonoidal[F[_], G[_]] extends ContravariantMonoidal[Nested[F, G, *]] {
def FG: ContravariantMonoidal[λ[α => F[G[α]]]]

Expand Down
53 changes: 53 additions & 0 deletions core/src/main/scala/cats/data/Op.scala
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,15 @@ sealed abstract private[data] class OpInstances extends OpInstances0 {
implicit def catsDataCategoryForOp[Arr[_, _]](implicit ArrC: Category[Arr]): Category[Op[Arr, *, *]] =
new OpCategory[Arr] { def Arr: Category[Arr] = ArrC }

implicit def catsDataDecidableForOp[Arr[_, _], R](implicit
ArrC: ArrowChoice[Arr],
monn: Monoid[R]
): Decidable[Op[Arr, R, *]] =
new OpDecidable[Arr, R] {
def Arr: ArrowChoice[Arr] = ArrC
def M: Monoid[R] = monn
}

implicit def catsDataEqForOp[Arr[_, _], A, B](implicit ArrEq: Eq[Arr[B, A]]): Eq[Op[Arr, A, B]] =
new OpEq[Arr, A, B] { def Arr: Eq[Arr[B, A]] = ArrEq }

Expand All @@ -52,6 +61,17 @@ sealed abstract private[data] class OpInstances extends OpInstances0 {
sealed abstract private[data] class OpInstances0 {
implicit def catsDataComposeForOp[Arr[_, _]](implicit ArrC: Compose[Arr]): Compose[Op[Arr, *, *]] =
new OpCompose[Arr] { def Arr: Compose[Arr] = ArrC }

implicit def catsDataContravariantMonoidalForOp[Arr[_, _], R](implicit
ArrC: Arrow[Arr],
M0: Monoid[R]
): ContravariantMonoidal[Op[Arr, R, *]] =
new OpContravariantMonoidal[Arr, R] { def Arr = ArrC; def M = M0 }
}

sealed abstract private[data] class OpInstances1 {
implicit def catsDataContravariantForOp[Arr[_, _], R](implicit ArrC: Arrow[Arr]): Contravariant[Op[Arr, R, *]] =
new OpContravariant[Arr, R] { def Arr = ArrC }
}

private[data] trait OpCategory[Arr[_, _]] extends Category[Op[Arr, *, *]] with OpCompose[Arr] {
Expand All @@ -67,6 +87,39 @@ private[data] trait OpCompose[Arr[_, _]] extends Compose[Op[Arr, *, *]] {
f.compose(g)
}

private[data] trait OpDecidable[Arr[_, _], R] extends Decidable[Op[Arr, R, *]] with OpContravariantMonoidal[Arr, R] {
implicit def Arr: ArrowChoice[Arr]
implicit def M: Monoid[R]

def sum[A, B](fa: Op[Arr, R, A], fb: Op[Arr, R, B]): Op[Arr, R, Either[A, B]] =
Op(Arr.choice(fa.run, fb.run))

override val zero: Op[Arr, R, Nothing] =
Op[Arr, R, Nothing](
Arr.lift[Nothing, R]((n: Nothing) => n: R)
)
}

private[data] trait OpContravariantMonoidal[Arr[_, _], R]
extends OpContravariant[Arr, R]
with ContravariantMonoidal[Op[Arr, R, *]] {
implicit def Arr: Arrow[Arr]
implicit def M: Monoid[R]

val unit: Op[Arr, R, Unit] =
Op(Arr.lift(Function.const(M.empty)))

def product[A, B](fa: Op[Arr, R, A], fb: Op[Arr, R, B]): Op[Arr, R, (A, B)] =
Op(Arr.compose(Arr.lift((M.combine _).tupled), Arr.split(fa.run, fb.run)))
}

private[data] trait OpContravariant[Arr[_, _], R] extends Contravariant[Op[Arr, R, *]] {
implicit def Arr: Arrow[Arr]

def contramap[A, B](fa: Op[Arr, R, A])(f: B => A): Op[Arr, R, B] =
Op(Arr.compose(fa.run, Arr.lift(f)))
}

private[data] trait OpEq[Arr[_, _], A, B] extends Eq[Op[Arr, A, B]] {
implicit def Arr: Eq[Arr[B, A]]

Expand Down
11 changes: 4 additions & 7 deletions core/src/main/scala/cats/data/OptionT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -542,7 +542,6 @@ sealed abstract private[data] class OptionTInstances extends OptionTInstances0 {
}

sealed abstract private[data] class OptionTInstances0 extends OptionTInstances1 {

// the Dummy type is to make this one more specific than catsDataMonadErrorMonadForOptionT on 2.13.x
// see https://github.com/typelevel/cats/pull/2335#issuecomment-408249775
implicit def catsDataMonadErrorForOptionT[F[_], E](implicit
Expand Down Expand Up @@ -671,12 +670,10 @@ private trait OptionTContravariantMonoidal[F[_]] extends ContravariantMonoidal[O

override def product[A, B](fa: OptionT[F, A], fb: OptionT[F, B]): OptionT[F, (A, B)] =
OptionT(
F.contramap(F.product(fa.value, fb.value))((t: Option[(A, B)]) =>
t match {
case Some((x, y)) => (Some(x), Some(y))
case None => (None, None)
}
)
F.contramap(F.product(fa.value, fb.value)) {
case Some((x, y)) => (Some(x), Some(y))
case None => (None, None)
}
)
}

Expand Down
Loading