Skip to content

Commit

Permalink
Add CommutativeApply and Applicative and an instance for Validated[Co…
Browse files Browse the repository at this point in the history
…mmutativeSemigroup, ?]
  • Loading branch information
Luka Jacobowitz committed Sep 24, 2017
1 parent 4a7f8f6 commit 0ea4ac0
Show file tree
Hide file tree
Showing 14 changed files with 172 additions and 8 deletions.
14 changes: 14 additions & 0 deletions core/src/main/scala/cats/CommutativeApplicative.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package cats

import simulacrum.typeclass

/**
* Commutative Applicative.
*
* Further than an Applicative, which just allows composition of independent effectful functions,
* in a Commutative Applicative those functions can be composed in any order, which guarantees
* that their effects do not interfere.
*
* Must obey the laws defined in cats.laws.CommutativeApplicativeLaws.
*/
@typeclass trait CommutativeApplicative[F[_]] extends Applicative[F] with CommutativeApply[F]
14 changes: 14 additions & 0 deletions core/src/main/scala/cats/CommutativeApply.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package cats

import simulacrum.typeclass

/**
* Commutative Apply.
*
* Further than an Apply, which just allows composition of independent effectful functions,
* in a Commutative Apply those functions can be composed in any order, which guarantees
* that their effects do not interfere.
*
* Must obey the laws defined in cats.laws.CommutativeApplyLaws.
*/
@typeclass trait CommutativeApply[F[_]] extends Apply[F]
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/CommutativeFlatMap.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ import simulacrum.typeclass
*
* Must obey the laws defined in cats.laws.CommutativeFlatMapLaws.
*/
@typeclass trait CommutativeFlatMap[F[_]] extends FlatMap[F]
@typeclass trait CommutativeFlatMap[F[_]] extends FlatMap[F] with CommutativeApply[F]
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/CommutativeMonad.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ import simulacrum.typeclass
*
* Must obey the laws defined in cats.laws.CommutativeMonadLaws.
*/
@typeclass trait CommutativeMonad[F[_]] extends Monad[F] with CommutativeFlatMap[F]
@typeclass trait CommutativeMonad[F[_]] extends Monad[F] with CommutativeFlatMap[F] with CommutativeApplicative[F]
15 changes: 15 additions & 0 deletions core/src/main/scala/cats/data/Validated.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package cats
package data

import cats.data.Validated.{Invalid, Valid}
import cats.kernel.CommutativeSemigroup

import scala.reflect.ClassTag
import scala.util.{Failure, Success, Try}
Expand Down Expand Up @@ -374,6 +375,20 @@ private[data] sealed abstract class ValidatedInstances1 extends ValidatedInstanc
def combine(x: Validated[A, B], y: Validated[A, B]): Validated[A, B] = x combine y
}

implicit def catsDataCommutativeApplicativeForValidated[E: CommutativeSemigroup]: CommutativeApplicative[Validated[E, ?]] =
new CommutativeApplicative[Validated[E, ?]] {
override def map[A, B](fa: Validated[E, A])(f: A => B): Validated[E, B] =
fa.map(f)

def pure[A](a: A): Validated[E, A] = Validated.valid(a)

def ap[A, B](ff: Validated[E, (A) => B])(fa: Validated[E, A]): Validated[E, B] =
fa.ap(ff)(Semigroup[E])

override def product[A, B](fa: Validated[E, A], fb: Validated[E, B]): Validated[E, (A, B)] =
fa.product(fb)(Semigroup[E])
}

implicit def catsDataPartialOrderForValidated[A: PartialOrder, B: PartialOrder]: PartialOrder[Validated[A, B]] =
new PartialOrder[Validated[A, B]] {
def partialCompare(x: Validated[A, B], y: Validated[A, B]): Double = x partialCompare y
Expand Down
12 changes: 12 additions & 0 deletions laws/src/main/scala/cats/laws/CommutativeApplicativeLaws.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package cats.laws

import cats.CommutativeApplicative

trait CommutativeApplicativeLaws[F[_]] extends CommutativeApplyLaws[F] with ApplicativeLaws[F] {
implicit override def F: CommutativeApplicative[F]
}

object CommutativeApplicativeLaws {
def apply[F[_]](implicit ev: CommutativeApplicative[F]): CommutativeApplicativeLaws[F] =
new CommutativeApplicativeLaws[F] { def F: CommutativeApplicative[F] = ev }
}
19 changes: 19 additions & 0 deletions laws/src/main/scala/cats/laws/CommutativeApplyLaws.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package cats.laws

import cats.CommutativeApply

/**
* Laws that must be obeyed by any `CommutativeApply`.
*/
trait CommutativeApplyLaws[F[_]] extends ApplyLaws[F] {
implicit override def F: CommutativeApply[F]

def applyCommutative[A, B, C](fa: F[A], fb: F[B], f: (A, B) => C): IsEq[F[C]] =
F.map2(fa, fb)(f) <-> F.map2(fb, fa)((b, a) => f(a, b))

}

object CommutativeApplyLaws {
def apply[F[_]](implicit ev: CommutativeApply[F]): CommutativeApplyLaws[F] =
new CommutativeApplyLaws[F] { def F: CommutativeApply[F] = ev }
}
2 changes: 1 addition & 1 deletion laws/src/main/scala/cats/laws/CommutativeFlatMapLaws.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ package laws
/**
* Laws that must be obeyed by any `CommutativeFlatMap`.
*/
trait CommutativeFlatMapLaws[F[_]] extends FlatMapLaws[F] {
trait CommutativeFlatMapLaws[F[_]] extends CommutativeApplyLaws[F] with FlatMapLaws[F] {
implicit override def F: CommutativeFlatMap[F]

def flatmapCommutative[A, B, C](fa: F[A], fb: F[B], g: (A, B) => F[C]): IsEq[F[C]] =
Expand Down
2 changes: 1 addition & 1 deletion laws/src/main/scala/cats/laws/CommutativeMonadLaws.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ package laws
/**
* Laws that must be obeyed by any `CommutativeMonad`.
*/
trait CommutativeMonadLaws[F[_]] extends MonadLaws[F] with CommutativeFlatMapLaws[F] {
trait CommutativeMonadLaws[F[_]] extends MonadLaws[F] with CommutativeFlatMapLaws[F] with CommutativeApplicativeLaws[F] {
implicit override def F: CommutativeMonad[F]
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package cats
package laws
package discipline

import cats.laws.discipline.CartesianTests.Isomorphisms
import org.scalacheck.{Arbitrary, Cogen, Prop}

trait CommutativeApplicativeTests[F[_]] extends CommutativeApplyTests[F] with ApplicativeTests[F] {

def laws: CommutativeApplicativeLaws[F]

def commutativeApplicative[A: Arbitrary: Eq, B: Arbitrary: Eq, C: Arbitrary: Eq](implicit
ArbFA: Arbitrary[F[A]],
ArbFB: Arbitrary[F[B]],
ArbFC: Arbitrary[F[C]],
ArbFAtoB: Arbitrary[F[A => B]],
ArbFBtoC: Arbitrary[F[B => C]],
CogenA: Cogen[A],
CogenB: Cogen[B],
CogenC: Cogen[C],
EqFA: Eq[F[A]],
EqFB: Eq[F[B]],
EqFC: Eq[F[C]],
EqFABC: Eq[F[(A, B, C)]],
EqFInt: Eq[F[Int]],
iso: Isomorphisms[F]
): RuleSet = {
new RuleSet {
def name: String = "commutative applicative"
def bases: Seq[(String, RuleSet)] = Nil
def parents: Seq[RuleSet] = Seq(applicative[A, B, C], commutativeApply[A, B, C])
def props: Seq[(String, Prop)] = Nil
}
}
}

object CommutativeApplicativeTests {
def apply[F[_]: CommutativeApplicative]: CommutativeApplicativeTests[F] =
new CommutativeApplicativeTests[F] {
def laws: CommutativeApplicativeLaws[F] = CommutativeApplicativeLaws[F]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package cats
package laws
package discipline

import cats.laws.discipline.CartesianTests.Isomorphisms
import org.scalacheck.{Arbitrary, Cogen, Prop}
import Prop._

trait CommutativeApplyTests[F[_]] extends ApplyTests[F] {
def laws: CommutativeApplyLaws[F]

def commutativeApply[A: Arbitrary: Eq, B: Arbitrary: Eq, C: Arbitrary: Eq](implicit
ArbFA: Arbitrary[F[A]],
ArbFB: Arbitrary[F[B]],
ArbFC: Arbitrary[F[C]],
ArbFAtoB: Arbitrary[F[A => B]],
ArbFBtoC: Arbitrary[F[B => C]],
CogenA: Cogen[A],
CogenB: Cogen[B],
CogenC: Cogen[C],
EqFA: Eq[F[A]],
EqFB: Eq[F[B]],
EqFC: Eq[F[C]],
EqFABC: Eq[F[(A, B, C)]],
EqFInt: Eq[F[Int]],
iso: Isomorphisms[F]
): RuleSet = {
new RuleSet {
def name: String = "commutative apply"
def bases: Seq[(String, RuleSet)] = Nil
def parents: Seq[RuleSet] = Seq(apply[A, B, C])
def props: Seq[(String, Prop)] = Seq(
"apply commutativity" -> forAll(laws.applyCommutative[A, B, C] _)
)
}
}

}

object CommutativeApplyTests {
def apply[F[_]: CommutativeFlatMap]: CommutativeApplyTests[F] =
new CommutativeApplyTests[F] {
def laws: CommutativeApplyLaws[F] = CommutativeApplyLaws[F]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import cats.laws.discipline.CartesianTests.Isomorphisms
import org.scalacheck.{Arbitrary, Cogen, Prop}
import Prop._

trait CommutativeFlatMapTests[F[_]] extends FlatMapTests[F] {
trait CommutativeFlatMapTests[F[_]] extends FlatMapTests[F] with CommutativeApplyTests[F] {
def laws: CommutativeFlatMapLaws[F]

def commutativeFlatMap[A: Arbitrary: Eq, B: Arbitrary: Eq, C: Arbitrary: Eq](implicit
Expand All @@ -28,7 +28,7 @@ trait CommutativeFlatMapTests[F[_]] extends FlatMapTests[F] {
new RuleSet {
def name: String = "commutative flatMap"
def bases: Seq[(String, RuleSet)] = Nil
def parents: Seq[RuleSet] = Seq(flatMap[A, B, C])
def parents: Seq[RuleSet] = Seq(flatMap[A, B, C], commutativeApply[A, B, C])
def props: Seq[(String, Prop)] = Seq(
"flatmap commutativity" -> forAll(laws.flatmapCommutative[A, B, C] _)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ package discipline
import cats.laws.discipline.CartesianTests.Isomorphisms
import org.scalacheck.{Arbitrary, Cogen, Prop}

trait CommutativeMonadTests[F[_]] extends MonadTests[F] with CommutativeFlatMapTests[F] {
trait CommutativeMonadTests[F[_]] extends MonadTests[F] with CommutativeFlatMapTests[F] with CommutativeApplicativeTests[F] {
def laws: CommutativeMonadLaws[F]

def commutativeMonad[A: Arbitrary: Eq, B: Arbitrary: Eq, C: Arbitrary: Eq](implicit
Expand All @@ -27,7 +27,7 @@ trait CommutativeMonadTests[F[_]] extends MonadTests[F] with CommutativeFlatMapT
new RuleSet {
def name: String = "commutative monad"
def bases: Seq[(String, RuleSet)] = Nil
def parents: Seq[RuleSet] = Seq(monad[A, B, C], commutativeFlatMap[A, B, C])
def parents: Seq[RuleSet] = Seq(monad[A, B, C], commutativeFlatMap[A, B, C], commutativeApplicative[A, B, C])
def props: Seq[(String, Prop)] = Nil
}
}
Expand Down
3 changes: 3 additions & 0 deletions tests/src/test/scala/cats/tests/ValidatedTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ class ValidatedTests extends CatsSuite {

checkAll("Validated[String, NonEmptyList[Int]]", GroupLaws[Validated[String, NonEmptyList[Int]]].semigroup)

checkAll("Validated[Int, Int]", CommutativeApplicativeTests[Validated[Int, ?]].commutativeApplicative[Int, Int, Int])
checkAll("CommutativeApplicative[Validated[Int, ?]]", SerializableTests.serializable(CommutativeApplicative[Validated[Int, ?]]))

{
implicit val L = ListWrapper.semigroup[String]
checkAll("Validated[ListWrapper[String], ?]", SemigroupKTests[Validated[ListWrapper[String], ?]].semigroupK[Int])
Expand Down

0 comments on commit 0ea4ac0

Please sign in to comment.