Skip to content

Commit

Permalink
Merge pull request #626 from philwills/const-kleisli-contravariant
Browse files Browse the repository at this point in the history
Add contravariant instances for Const and Kleisli
  • Loading branch information
non committed Nov 12, 2015
2 parents 5d64d09 + 697c4ad commit d50e1d4
Show file tree
Hide file tree
Showing 4 changed files with 23 additions and 3 deletions.
7 changes: 7 additions & 0 deletions core/src/main/scala/cats/data/Const.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package cats
package data

import cats.functor.Contravariant

/**
* [[Const]] is a phantom type, it does not contain a value of its second type parameter `B`
* [[Const]] can be seen as a type level version of `Function.const[A, B]: A => B => A`
Expand Down Expand Up @@ -46,6 +48,11 @@ private[data] sealed abstract class ConstInstances extends ConstInstances0 {
def show(f: Const[A, B]): String = f.show
}

implicit def constContravariant[C]: Contravariant[Const[C, ?]] = new Contravariant[Const[C, ?]] {
override def contramap[A, B](fa: Const[C, A])(f: (B) => A): Const[C, B] =
fa.retag[B]
}

implicit def constTraverse[C]: Traverse[Const[C, ?]] = new Traverse[Const[C, ?]] {
def traverse[G[_]: Applicative, A, B](fa: Const[C, A])(f: A => G[B]): G[Const[C, B]] =
fa.traverse(f)
Expand Down
8 changes: 7 additions & 1 deletion core/src/main/scala/cats/data/Kleisli.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package cats
package data

import cats.arrow.{Arrow, Choice, Split}
import cats.functor.Strong
import cats.functor.{Contravariant, Strong}

/**
* Represents a function `A => F[B]`.
Expand Down Expand Up @@ -116,6 +116,12 @@ private[data] sealed abstract class KleisliInstances extends KleisliInstances0 {
def local[B](f: A => A)(fa: Kleisli[F, A, B]): Kleisli[F, A, B] =
Kleisli(f.andThen(fa.run))
}

implicit def kleisliContravariant[F[_], C]: Contravariant[Kleisli[F, ?, C]] =
new Contravariant[Kleisli[F, ?, C]] {
override def contramap[A, B](fa: Kleisli[F, A, C])(f: (B) => A): Kleisli[F, B, C] =
fa.lmap(f)
}
}

private[data] sealed abstract class KleisliInstances0 extends KleisliInstances1 {
Expand Down
6 changes: 5 additions & 1 deletion tests/src/test/scala/cats/tests/ConstTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ package tests
import algebra.laws.{GroupLaws, OrderLaws}

import cats.data.{Const, NonEmptyList}
import cats.laws.discipline.{ApplyTests, ApplicativeTests, SerializableTests, TraverseTests}
import cats.functor.Contravariant
import cats.laws.discipline._
import cats.laws.discipline.arbitrary.{constArbitrary, oneAndArbitrary}

class ConstTests extends CatsSuite {
Expand All @@ -31,4 +32,7 @@ class ConstTests extends CatsSuite {
checkAll("Const[Map[Int, Int], String]", OrderLaws[Const[Map[Int, Int], String]].eqv)
checkAll("PartialOrder[Const[Set[Int], String]]", OrderLaws[Const[Set[Int], String]].partialOrder)
checkAll("Order[Const[Int, String]]", OrderLaws[Const[Int, String]].order)

checkAll("Const[String, Int]", ContravariantTests[Const[String, ?]].contravariant[Int, Int, Int])
checkAll("Contravariant[Const[String, ?]]", SerializableTests.serializable(Contravariant[Const[String, ?]]))
}
5 changes: 4 additions & 1 deletion tests/src/test/scala/cats/tests/KleisliTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package tests

import cats.arrow.{Arrow, Choice, Split}
import cats.data.Kleisli
import cats.functor.Strong
import cats.functor.{Contravariant, Strong}
import cats.laws.discipline._
import cats.laws.discipline.arbitrary._
import cats.laws.discipline.eq._
Expand Down Expand Up @@ -93,6 +93,9 @@ class KleisliTests extends CatsSuite {
checkAll("SemigroupK[Lambda[A => Kleisli[Option, A, A]]]", SerializableTests.serializable(kleisliSemigroupK))
}

checkAll("Kleisli[Option, ?, Int]", ContravariantTests[Kleisli[Option, ?, Int]].contravariant[Int, Int, Int])
checkAll("Contravariant[Kleisli[Option, ?, Int]]", SerializableTests.serializable(Contravariant[Kleisli[Option, ?, Int]]))

test("local composes functions") {
forAll { (f: Int => Option[String], g: Int => Int, i: Int) =>
f(g(i)) should === (Kleisli.local[Option, String, Int](g)(Kleisli.function(f)).run(i))
Expand Down

0 comments on commit d50e1d4

Please sign in to comment.