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

Follow up to #751 #757

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
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
17 changes: 16 additions & 1 deletion core/src/main/scala/cats/Alternative.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,20 @@ package cats

import simulacrum.typeclass

@typeclass trait Alternative[F[_]] extends Applicative[F] with MonoidK[F]
@typeclass trait Alternative[F[_]] extends Applicative[F] with MonoidK[F] { self =>
/**
* Compose two Alternative intsances.
*/
def compose[G[_]](implicit GG: Alternative[G]): Alternative[λ[α => F[G[α]]]] =
Copy link
Contributor

Choose a reason for hiding this comment

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

I think the GG constraint can be relaxed to Applicative while still returning an Alternative (also on CompositeAlternative below).

Copy link
Member Author

Choose a reason for hiding this comment

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

Agreed ... updated accordingly ...

new CompositeAlternative[F, G] {
implicit def F: Alternative[F] = self
implicit def G: Alternative[G] = GG
}
}

trait CompositeAlternative[F[_], G[_]]
extends Alternative[λ[α => F[G[α]]]] with CompositeApplicative[F, G] with CompositeMonoidK[F, G] {

implicit def F: Alternative[F]
implicit def G: Alternative[G]
}
21 changes: 21 additions & 0 deletions core/src/main/scala/cats/MonoidK.scala
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,19 @@ import simulacrum.typeclass
*/
def empty[A]: F[A]

/**
* Compose this MonoidK with an arbitrary type constructor
*/
override def composedWith[G[_]]: MonoidK[λ[α => F[G[α]]]] =
new CompositeMonoidK[F, G] {
implicit def F: MonoidK[F] = self
}

/**
* Compose two MonoidK instances.
*/
def compose[G[_]](implicit GG: MonoidK[G]): MonoidK[λ[α => F[G[α]]]] = composedWith[G]

/**
* Given a type A, create a concrete Monoid[F[A]].
*/
Expand All @@ -38,3 +51,11 @@ import simulacrum.typeclass
def combine(x: F[A], y: F[A]): F[A] = self.combine(x, y)
}
}

trait CompositeMonoidK[F[_],G[_]]
extends MonoidK[λ[α => F[G[α]]]] with CompositeSemigroupK[F, G] {

implicit def F: MonoidK[F]

def empty[A]: F[G[A]] = F.empty
}
21 changes: 17 additions & 4 deletions core/src/main/scala/cats/SemigroupK.scala
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,18 @@ import simulacrum.{op, typeclass}
def combine[A](x: F[A], y: F[A]): F[A]

/**
* Compose two SemigroupK intsances.
* Compose this SemigroupK with an arbitrary type constructor
*/
def compose[G[_]: SemigroupK]: SemigroupK[λ[α => F[G[α]]]] =
new SemigroupK[λ[α => F[G[α]]]] {
def combine[A](x: F[G[A]], y: F[G[A]]): F[G[A]] = self.combine(x, y)
def composedWith[G[_]]: SemigroupK[λ[α => F[G[α]]]] =
new CompositeSemigroupK[F, G] {
implicit def F: SemigroupK[F] = self
}

/**
* Compose two SemigroupK instances.
*/
def compose[G[_]](implicit GG: SemigroupK[G]): SemigroupK[λ[α => F[G[α]]]] = composedWith[G]

/**
* Given a type A, create a concrete Semigroup[F[A]].
*/
Expand All @@ -44,3 +49,11 @@ import simulacrum.{op, typeclass}
def combine(x: F[A], y: F[A]): F[A] = self.combine(x, y)
}
}

trait CompositeSemigroupK[F[_],G[_]]
extends SemigroupK[λ[α => F[G[α]]]] {

implicit def F: SemigroupK[F]

def combine[A](x: F[G[A]], y: F[G[A]]): F[G[A]] = F.combine(x, y)
}
6 changes: 6 additions & 0 deletions core/src/main/scala/cats/data/OneAnd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,12 @@ trait OneAndLowPriority1 extends OneAndLowPriority0 {
def map[A, B](fa: OneAnd[F, A])(f: A => B): OneAnd[F, B] =
OneAnd(f(fa.head), F.map(fa.tail)(f))
}

implicit def oneAndReducible[F[_]](implicit foldable: Foldable[F]): Reducible[OneAnd[F, ?]] =
new NonEmptyReducible[OneAnd[F, ?], F] {
def split[A](fa: OneAnd[F, A]): (A, F[A]) = (fa.head, fa.tail)
}

}

object OneAnd extends OneAndInstances
31 changes: 20 additions & 11 deletions tests/src/test/scala/cats/tests/ComposeTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ package cats
package tests

import cats.data.{ NonEmptyList, NonEmptyVector, OneAnd }
import cats.laws.discipline.{ ApplicativeTests, FoldableTests, MonoidalTests, SemigroupKTests, arbitrary, eq }, arbitrary._, eq._
import cats.laws.discipline.{ AlternativeTests, ApplicativeTests, FoldableTests, MonoidKTests, MonoidalTests, SemigroupKTests }
import cats.laws.discipline.{ arbitrary, eq }, arbitrary._, eq._
import org.scalacheck.Arbitrary

class ComposeTests extends CatsSuite {
Expand All @@ -12,6 +13,15 @@ class ComposeTests extends CatsSuite {
implicit override val generatorDrivenConfig: PropertyCheckConfiguration =
PropertyCheckConfig(maxSize = 5, minSuccessful = 20)

{
// Alternative composition

implicit val alternativeListVector: Alternative[Lambda[A => List[Vector[A]]]] = Alternative[List] compose Alternative[Vector]
implicit val iso = MonoidalTests.Isomorphisms.invariant[Lambda[A => List[Vector[A]]]]

checkAll("Alternative[Lambda[A => List[Vector[A]]]]", AlternativeTests[Lambda[A => List[Vector[A]]]].alternative[Int, Int, Int])
}

{
// Applicative composition

Expand All @@ -30,19 +40,18 @@ class ComposeTests extends CatsSuite {
}

{
// Reducible composition
// MonoidK composition

implicit val monoidKListVector: MonoidK[Lambda[A => List[Vector[A]]]] = MonoidK[List] compose MonoidK[Vector]

val nelReducible =
new NonEmptyReducible[NonEmptyList, List] {
def split[A](fa: NonEmptyList[A]): (A, List[A]) = (fa.head, fa.tail)
}
checkAll("MonoidK[Lambda[A => List[Vector[A]]]]", MonoidKTests[Lambda[A => List[Vector[A]]]].monoidK[Int])
}

val nevReducible =
new NonEmptyReducible[NonEmptyVector, Vector] {
def split[A](fa: NonEmptyVector[A]): (A, Vector[A]) = (fa.head, fa.tail)
}
{
// Reducible composition

implicit val reducibleListVector: Reducible[Lambda[A => NonEmptyList[NonEmptyVector[A]]]] = nelReducible compose nevReducible
implicit val reducibleListVector: Reducible[Lambda[A => NonEmptyList[NonEmptyVector[A]]]] =
Reducible[NonEmptyList] compose Reducible[NonEmptyVector]

// No Reducible-specific laws, so check the Foldable laws are satisfied
checkAll("Reducible[Lambda[A => List[Vector[A]]]]", FoldableTests[Lambda[A => NonEmptyList[NonEmptyVector[A]]]].foldable[Int, Int])
Expand Down