Skip to content

Commit

Permalink
Derive Contravariant and Invariant (#346)
Browse files Browse the repository at this point in the history
  • Loading branch information
TimWSpence authored May 24, 2021
1 parent 970ec37 commit 9ffcece
Show file tree
Hide file tree
Showing 11 changed files with 95 additions and 27 deletions.
5 changes: 4 additions & 1 deletion core/src/main/scala-3/cats/derived/all.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,20 @@ package cats.derived
object all extends
CommutativeMonoidDerivation,
CommutativeSemigroupDerivation,
ContravariantDerivation,
EmptyDerivation,
EmptyKDerivation,
EqDerivation,
FoldableDerivation,
FunctorDerivation,
HashDerivation,
InvariantDerivation,
MonoidDerivation,
MonoidKDerivation,
OrderDerivation,
PartialOrderDerivation,
SemigroupDerivation,
SemigroupKDerivation,
ShowDerivation,
TraverseDerivation
TraverseDerivation,
Instances
18 changes: 18 additions & 0 deletions core/src/main/scala-3/cats/derived/contravariant.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package cats.derived

import cats.Contravariant
import shapeless3.deriving.K1

object contravariant extends ContravariantDerivation, Instances

trait GenericContravariant[T[x[_]] <: Contravariant[x], F[_]](using inst: K1.Instances[T, F])
extends Contravariant[F]:

def contramap[A, B](fa: F[A])(f: B => A): F[B] = inst.map(fa)(
[t[_]] => (contra: T[t], t0: t[A]) => contra.contramap(t0)(f)
)

trait ContravariantDerivation:
extension (F: Contravariant.type)
inline def derived[F[_]](using gen: K1.Generic[F]): Contravariant[F] =
new GenericContravariant[Contravariant, F]{}
6 changes: 1 addition & 5 deletions core/src/main/scala-3/cats/derived/foldable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package cats.derived
import cats.{Eval, Foldable}
import shapeless3.deriving.{Const, Continue, K1}

object foldable extends FoldableDerivation
object foldable extends FoldableDerivation, Instances

trait ProductFoldable[T[x[_]] <: Foldable[x], F[_]](using inst: K1.ProductInstances[T, F])
extends Foldable[F]:
Expand Down Expand Up @@ -41,7 +41,3 @@ trait FoldableDerivation:

given coproductFoldable[F[_]](using inst: => K1.CoproductInstances[Foldable, F]): Foldable[F] =
new CoproductFoldable[Foldable, F]{}

given [X]: Foldable[Const[X]] with
def foldLeft[A, B](fa: X, b: B)(f: (B, A) => B): B = b
def foldRight[A, B](fa: X, lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = lb
5 changes: 1 addition & 4 deletions core/src/main/scala-3/cats/derived/functor.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package cats.derived
import cats.Functor
import shapeless3.deriving.{Const, K1}

object functor extends FunctorDerivation
object functor extends FunctorDerivation, Instances

trait GenericFunctor[T[x[_]] <: Functor[x], F[_]](using inst: K1.Instances[T, F])
extends Functor[F]:
Expand All @@ -17,6 +17,3 @@ trait FunctorDerivation:
extension (F: Functor.type)
inline def derived[F[_]](using gen: K1.Generic[F]): Functor[F] =
new GenericFunctor[Functor, F]{}

given [X]: Functor[Const[X]] with
def map[A, B](fa: X)(f: A => B): X = fa
29 changes: 29 additions & 0 deletions core/src/main/scala-3/cats/derived/instances.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package cats.derived

import cats.*
import shapeless3.deriving.Const

private[derived] trait Instances extends Instances1:

given [X](using X: Monoid[X]): MonoidK[Const[X]] with
def empty[A]: Const[X][A] = X.empty

def combineK[A](x: Const[X][A], y: Const[X][A]): Const[X][A] =
X.combine(x, y)

given [X]: Traverse[Const[X]] with Contravariant[Const[X]] with
override def map[A, B](fa: Const[X][A])(f: A => B): Const[X][B] = fa

def contramap[A, B](fa: X)(f: B => A): X = fa

def foldLeft[A, B](fa: Const[X][A], b: B)(f: (B, A) => B): B = b

def foldRight[A, B](fa: Const[X][A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = lb

def traverse[G[_]: Applicative, A, B](fa: Const[X][A])(f: A => G[B]): G[Const[X][B]] =
Applicative[G].pure(fa)

private[derived] trait Instances1:
given [X](using X: Semigroup[X]): SemigroupK[Const[X]] with
def combineK[A](x: Const[X][A], y: Const[X][A]): Const[X][A] =
X.combine(x, y)
17 changes: 17 additions & 0 deletions core/src/main/scala-3/cats/derived/invariant.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package cats.derived

import cats.Invariant
import shapeless3.deriving.K1

object invariant extends InvariantDerivation, Instances

trait GenericInvariant[T[x[_]] <: Invariant[x], F[_]](using inst: K1.Instances[T, F])
extends Invariant[F]:
def imap[A, B](fa: F[A])(f: A => B)(g: B => A): F[B] = inst.map(fa)(
[t[_]] => (inv: T[t], t0: t[A]) => inv.imap(t0)(f)(g)
)

trait InvariantDerivation:
extension (F: Invariant.type)
inline def derived[F[_]](using gen: K1.Generic[F]): Invariant[F] =
new GenericInvariant[Invariant, F]{}
6 changes: 1 addition & 5 deletions core/src/main/scala-3/cats/derived/monoidk.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package cats.derived
import cats.{Monoid, MonoidK}
import shapeless3.deriving.{Const, K1}

object monoidk extends MonoidKDerivation
object monoidk extends MonoidKDerivation, Instances

trait ProductMonoidK[T[x[_]] <: MonoidK[x], F[_]](using inst: K1.ProductInstances[T, F])
extends ProductSemigroupK[T, F], MonoidK[F]:
Expand All @@ -12,7 +12,3 @@ trait ProductMonoidK[T[x[_]] <: MonoidK[x], F[_]](using inst: K1.ProductInstance
trait MonoidKDerivation:
extension (F: MonoidK.type)
inline def derived[F[_]](using gen: K1.Generic[F]): MonoidK[F] = ???

given [X](using X: Monoid[X]): MonoidK[Const[X]] with
def empty[A]: X = X.empty
def combineK[A](x: X, y: X): X = X.combine(x, y)
6 changes: 1 addition & 5 deletions core/src/main/scala-3/cats/derived/semigroupk.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package cats.derived
import cats.{Semigroup, SemigroupK}
import shapeless3.deriving.{Const, K1}

object semigroupk extends SemigroupKDerivation
object semigroupk extends SemigroupKDerivation, Instances

trait ProductSemigroupK[T[x[_]] <: SemigroupK[x], F[_]](using inst: K1.ProductInstances[T, F])
extends SemigroupK[F]:
Expand All @@ -15,7 +15,3 @@ trait SemigroupKDerivation:
extension (F: SemigroupK.type)
inline def derived[F[_]](using gen: K1.ProductGeneric[F]): SemigroupK[F] =
new ProductSemigroupK[SemigroupK, F]{}

given [X](using X: Semigroup[X]): SemigroupK[Const[X]] with
def combineK[A](x: X, y: X): X =
X.combine(x, y)
8 changes: 1 addition & 7 deletions core/src/main/scala-3/cats/derived/traverse.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package cats.derived
import cats.{Applicative, Eval, Traverse}
import shapeless3.deriving.{Const, Continue, K1}

object traverse extends TraverseDerivation
object traverse extends TraverseDerivation, Instances

trait ProductTraverse[T[x[_]] <: Traverse[x], F[_]](using inst: K1.ProductInstances[T, F])
extends GenericFunctor[T, F], ProductFoldable[T, F], Traverse[F]:
Expand Down Expand Up @@ -31,9 +31,3 @@ trait TraverseDerivation:

given coproductTraverse[F[_]](using inst: => K1.CoproductInstances[Traverse, F]): Traverse[F] =
new CoproductTraverse[Traverse, F]{}

given [X]: Traverse[Const[X]] with
override def map[A, B](fa: X)(f: A => B): X = fa
def foldLeft[A, B](fa: X, b: B)(f: (B, A) => B): B = b
def foldRight[A, B](fa: X, lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = lb
def traverse[G[_]: Applicative, A, B](fa: X)(f: A => G[B]): G[X] = Applicative[G].pure(fa)
11 changes: 11 additions & 0 deletions core/src/test/scala-3/cats/derived/ContravariantTests.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package cats.derived

import cats.Contravariant
import cats.derived.all._
import cats.derived.all.given

class ContravariantTests {

case class Foo[A](f: A => String) derives Contravariant

}
11 changes: 11 additions & 0 deletions core/src/test/scala-3/cats/derived/InvariantTests.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package cats.derived

import cats.Invariant
import cats.derived.all._
import cats.derived.all.given

class InvariantTests {

case class Foo[A](bar: String, baz: Option[A]) derives Invariant

}

0 comments on commit 9ffcece

Please sign in to comment.