Skip to content

Commit

Permalink
Add ZipNEL and ZipNEV and Parallel instances
Browse files Browse the repository at this point in the history
  • Loading branch information
Luka Jacobowitz committed Sep 2, 2017
1 parent d571771 commit b311ab9
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 0 deletions.
27 changes: 27 additions & 0 deletions core/src/main/scala/cats/data/NonEmptyList.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cats
package data

import cats.data.NonEmptyList.ZipNonEmptyList
import cats.instances.list._
import cats.syntax.order._

Expand Down Expand Up @@ -383,6 +384,20 @@ object NonEmptyList extends NonEmptyListInstances {

def fromReducible[F[_], A](fa: F[A])(implicit F: Reducible[F]): NonEmptyList[A] =
F.toNonEmptyList(fa)

class ZipNonEmptyList[A](val value: NonEmptyList[A]) extends AnyVal

object ZipNonEmptyList {
implicit val zipNelApplicative: Applicative[ZipNonEmptyList] = new Applicative[ZipNonEmptyList] {
def pure[A](x: A): ZipNonEmptyList[A] = new ZipNonEmptyList(NonEmptyList.one(x))
def ap[A, B](ff: ZipNonEmptyList[A => B])(fa: ZipNonEmptyList[A]): ZipNonEmptyList[B] =
new ZipNonEmptyList(ff.value.zipWith(fa.value)(_ apply _))
}

implicit def zipNelEq[A: Eq]: Eq[ZipNonEmptyList[A]] = Eq.by(_.value)
implicit def zipNelShow[A](implicit ev: Show[A]): Show[ZipNonEmptyList[A]] =
Show.show(a => s"ZipList(${Show[NonEmptyList[A]].show(a.value)})")
}
}

private[data] sealed trait NonEmptyListInstances extends NonEmptyListInstances0 {
Expand Down Expand Up @@ -479,6 +494,18 @@ private[data] sealed trait NonEmptyListInstances extends NonEmptyListInstances0
new NonEmptyListOrder[A] {
val A0 = A
}

implicit def catsDataParallelForNonEmptyList[A]: Parallel[NonEmptyList, ZipNonEmptyList] =
new Parallel[NonEmptyList, ZipNonEmptyList] {

def applicative: Applicative[ZipNonEmptyList] = ZipNonEmptyList.zipNelApplicative

def sequential(implicit M: Monad[NonEmptyList]): ZipNonEmptyList ~> NonEmptyList =
λ[ZipNonEmptyList ~> NonEmptyList](_.value)

def parallel(implicit M: Monad[NonEmptyList]): NonEmptyList ~> ZipNonEmptyList =
λ[NonEmptyList ~> ZipNonEmptyList](nel => new ZipNonEmptyList(nel))
}
}

private[data] sealed trait NonEmptyListInstances0 extends NonEmptyListInstances1 {
Expand Down
27 changes: 27 additions & 0 deletions core/src/main/scala/cats/data/NonEmptyVector.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cats
package data

import cats.data.NonEmptyVector.ZipNonEmptyVector
import scala.annotation.tailrec
import scala.collection.immutable.{TreeSet, VectorBuilder}
import cats.instances.vector._
Expand Down Expand Up @@ -304,6 +305,18 @@ private[data] sealed trait NonEmptyVectorInstances {
implicit def catsDataSemigroupForNonEmptyVector[A]: Semigroup[NonEmptyVector[A]] =
catsDataInstancesForNonEmptyVector.algebra

implicit def catsDataParallelForNonEmptyVector[A]: Parallel[NonEmptyVector, ZipNonEmptyVector] =
new Parallel[NonEmptyVector, ZipNonEmptyVector] {

def applicative: Applicative[ZipNonEmptyVector] = ZipNonEmptyVector.zipNevApplicative

def sequential(implicit M: Monad[NonEmptyVector]): ZipNonEmptyVector ~> NonEmptyVector =
λ[ZipNonEmptyVector ~> NonEmptyVector](_.value)

def parallel(implicit M: Monad[NonEmptyVector]): NonEmptyVector ~> ZipNonEmptyVector =
λ[NonEmptyVector ~> ZipNonEmptyVector](nev => new ZipNonEmptyVector(nev))
}

}

object NonEmptyVector extends NonEmptyVectorInstances {
Expand All @@ -328,4 +341,18 @@ object NonEmptyVector extends NonEmptyVectorInstances {
def fromVectorUnsafe[A](vector: Vector[A]): NonEmptyVector[A] =
if (vector.nonEmpty) new NonEmptyVector(vector)
else throw new IllegalArgumentException("Cannot create NonEmptyVector from empty vector")

class ZipNonEmptyVector[A](val value: NonEmptyVector[A])

object ZipNonEmptyVector {
implicit val zipNevApplicative: Applicative[ZipNonEmptyVector] = new Applicative[ZipNonEmptyVector] {
def pure[A](x: A): ZipNonEmptyVector[A] = new ZipNonEmptyVector(NonEmptyVector.one(x))
def ap[A, B](ff: ZipNonEmptyVector[A => B])(fa: ZipNonEmptyVector[A]): ZipNonEmptyVector[B] =
new ZipNonEmptyVector(ff.value.zipWith(fa.value)(_ apply _))
}

implicit def zipNevEq[A: Eq]: Eq[ZipNonEmptyVector[A]] = Eq.by(_.value)
implicit def zipNevShow[A](implicit ev: Show[A]): Show[ZipNonEmptyVector[A]] =
Show.show(a => s"ZipVector(${Show[NonEmptyVector[A]].show(a.value)})")
}
}
14 changes: 14 additions & 0 deletions laws/src/main/scala/cats/laws/discipline/Arbitrary.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package cats
package laws
package discipline

import cats.data.NonEmptyList.ZipNonEmptyList
import cats.data.NonEmptyVector.ZipNonEmptyVector
import scala.util.{Failure, Success, Try}
import cats.data._
import org.scalacheck.{Arbitrary, Cogen, Gen}
Expand Down Expand Up @@ -48,12 +50,24 @@ object arbitrary extends ArbitraryInstances0 {
implicit def catsLawsCogenForNonEmptyVector[A](implicit A: Cogen[A]): Cogen[NonEmptyVector[A]] =
Cogen[Vector[A]].contramap(_.toVector)

implicit def catsLawsArbitraryForZipNonEmptyVector[A](implicit A: Arbitrary[A]): Arbitrary[ZipNonEmptyVector[A]] =
Arbitrary(implicitly[Arbitrary[NonEmptyVector[A]]].arbitrary.map(nev => new ZipNonEmptyVector(nev)))

implicit def catsLawsCogenForZipNonEmptyVector[A](implicit A: Cogen[A]): Cogen[ZipNonEmptyVector[A]] =
Cogen[NonEmptyVector[A]].contramap(_.value)

implicit def catsLawsArbitraryForNonEmptyList[A](implicit A: Arbitrary[A]): Arbitrary[NonEmptyList[A]] =
Arbitrary(implicitly[Arbitrary[List[A]]].arbitrary.flatMap(fa => A.arbitrary.map(a => NonEmptyList(a, fa))))

implicit def catsLawsCogenForNonEmptyList[A](implicit A: Cogen[A]): Cogen[NonEmptyList[A]] =
Cogen[List[A]].contramap(_.toList)

implicit def catsLawsArbitraryForZipNonEmptyList[A](implicit A: Arbitrary[A]): Arbitrary[ZipNonEmptyList[A]] =
Arbitrary(implicitly[Arbitrary[NonEmptyList[A]]].arbitrary.map(nel => new ZipNonEmptyList(nel)))

implicit def catsLawsCogenForZipNonEmptyList[A](implicit A: Cogen[A]): Cogen[ZipNonEmptyList[A]] =
Cogen[NonEmptyList[A]].contramap(_.value)

implicit def catsLawsArbitraryForEitherT[F[_], A, B](implicit F: Arbitrary[F[Either[A, B]]]): Arbitrary[EitherT[F, A, B]] =
Arbitrary(F.arbitrary.map(EitherT(_)))

Expand Down
16 changes: 16 additions & 0 deletions tests/src/test/scala/cats/tests/ParallelTests.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package cats


import cats.data.NonEmptyList.ZipNonEmptyList
import cats.data.NonEmptyVector.ZipNonEmptyVector
import cats.data._
import cats.tests.CatsSuite
import org.scalatest.FunSuite
Expand Down Expand Up @@ -58,10 +60,24 @@ class ParallelTests extends CatsSuite with ApplicativeErrorForEitherTest {

}

test("ParMap over NonEmptyList should be consistent with zip") {
forAll { (as: NonEmptyList[Int], bs: NonEmptyList[Int], cs: NonEmptyList[Int]) =>
(as, bs, cs).parMapN(_ + _ + _) should === (as.zipWith(bs)(_ + _).zipWith(cs)(_ + _))
}
}

test("ParMap over NonEmptyVector should be consistent with zip") {
forAll { (as: NonEmptyVector[Int], bs: NonEmptyVector[Int], cs: NonEmptyVector[Int]) =>
(as, bs, cs).parMapN(_ + _ + _) should === (as.zipWith(bs)(_ + _).zipWith(cs)(_ + _))
}
}

checkAll("Parallel[Either[String, ?], Validated[String, ?]]", ParallelTypeclassTests[Either[String, ?], Validated[String, ?], Int].parallel)
checkAll("Parallel[OptionT[M, ?], Nested[F, Option, ?]]", ParallelTypeclassTests[OptionT[Either[String, ?], ?], Nested[Validated[String, ?], Option, ?], Int].parallel)
checkAll("Parallel[EitherT[M, String, ?], Nested[F, Validated[String, ?], ?]]", ParallelTypeclassTests[EitherT[Either[String, ?], String, ?], Nested[Validated[String, ?], Validated[String, ?], ?], Int].parallel)
checkAll("Parallel[WriterT[M, Int, ?], WriterT[F, Int, ?]]", ParallelTypeclassTests[WriterT[Either[String, ?], Int, ?], WriterT[Validated[String, ?], Int, ?], Int].parallel)
checkAll("Parallel[NonEmptyList, ZipNonEmptyList]", ParallelTypeclassTests[NonEmptyList, ZipNonEmptyList, Int].parallel)
checkAll("Parallel[NonEmptyVector, ZipNonEmptyVector]", ParallelTypeclassTests[NonEmptyVector, ZipNonEmptyVector, Int].parallel)

{
implicit def kleisliEq[F[_], A, B](implicit A: Arbitrary[A], FB: Eq[F[B]]): Eq[Kleisli[F, A, B]] =
Expand Down

0 comments on commit b311ab9

Please sign in to comment.