Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
Badmoonz committed Nov 22, 2020
1 parent 26ae9b4 commit 7d1cae7
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 4 deletions.
7 changes: 4 additions & 3 deletions core/shared/src/main/scala/zio/prelude/AssociativeBoth.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1201,10 +1201,11 @@ object AssociativeBoth extends LawfulF.Invariant[AssociativeBothDeriveEqualInvar
}

/**
* The `AssociativeBoth` instance for `ZIO`.
* The `IdentityBoth` instance for `ZIO`.
*/
implicit def ZIOAssociativeBoth[R, E]: AssociativeBoth[({ type lambda[+a] = ZIO[R, E, a] })#lambda] =
new AssociativeBoth[({ type lambda[+a] = ZIO[R, E, a] })#lambda] {
implicit def ZIOIdentityBoth[R, E]: IdentityBoth[({ type lambda[+a] = ZIO[R, E, a] })#lambda] =
new IdentityBoth[({ type lambda[+a] = ZIO[R, E, a] })#lambda] {
def any: ZIO[R, E, Any] = ZIO.unit
def both[A, B](fa: => ZIO[R, E, A], fb: => ZIO[R, E, B]): ZIO[R, E, (A, B)] = fa zip fb
}

Expand Down
18 changes: 18 additions & 0 deletions core/shared/src/main/scala/zio/prelude/Traversable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,24 @@ trait Traversable[F[+_]] extends Covariant[F] {
}
}

def forany[G[+ _] : AssociativeEither : IdentityBoth : Covariant, A, B](fa: F[A])(f: A => G[B]): G[Option[B]] =
foldLeft(fa)(None: Option[G[B]]) {
case (Some(s), v) => Some(s.orElse(f(v)))
case (None, v) => Some(f(v))
} match {
case Some(x) => x.map[Option[B]](Some(_))
case None => IdentityBoth[G].any.as(None)
}

def foranyPar[G[+_] : CommutativeEither : IdentityBoth : Covariant, A, B](fa: F[A])(f: A => G[B]): G[Option[B]] =
foldLeft(fa)(None : Option[G[B]]) {
case (Some(s), v) => Some(s.orElsePar(f(v)))
case (None, v) => Some(f(v))
} match {
case Some(x) => x.map[Option[B]](Some(_))
case None => IdentityBoth[G].any.as(None)
}

/**
* Returns whether the collection is empty.
*/
Expand Down
54 changes: 53 additions & 1 deletion core/shared/src/test/scala/zio/prelude/TraversableSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ package zio.prelude

import zio.random.Random
import zio.test._
import zio.test.Assertion._
import zio.test.laws._
import zio.{ Chunk, NonEmptyChunk }
import zio.{Chunk, IO, NonEmptyChunk}

import scala.collection.Set

object TraversableSpec extends DefaultRunnableSpec {

Expand All @@ -25,6 +28,12 @@ object TraversableSpec extends DefaultRunnableSpec {
val genIntFunction: Gen[Random, Int => Int] =
Gen.function(genInt)

val genOptIntFunction: Gen[Random, Int => Option[Int]] =
Gen.boolean.zipWith(Gen.function(genInt)) {
case (true, f) => x => Some(f(x))
case (false, _) => _ => None
}

val genIntFunction2: Gen[Random, (Int, Int) => Int] =
Gen.function2(genInt)

Expand Down Expand Up @@ -115,6 +124,49 @@ object TraversableSpec extends DefaultRunnableSpec {
assert(actual)(equalTo(expected))
}
},
testM("forany") {
check(genList, genOptIntFunction) { case (as, f) =>
val actual = Traversable[List].forany(as)(f).flatten
val expected = as.map(f).flatten.headOption
assert(actual)(equalTo(expected))
}
},
testM("forany IO") {
check(genList, genOptIntFunction) { case (as, f) =>
val actual = zio.Runtime.default.unsafeRun(Traversable[List].forany(as)(f.map {
case Some(x) => IO.succeedNow(x)
case None => IO.fail(())
}(Invariant.Function1Covariant)).option)
val expected : Option[Option[Int]] = {
val allResults = as.map(f)
if(allResults.nonEmpty && allResults.forall(_.isEmpty))
None
else
Some(allResults.flatten.headOption)
}
assert(actual)(equalTo(expected))
}
},
testM("foranyPar IO") {
check(genList, genOptIntFunction) { case (as, f) =>
val actual = zio.Runtime.default.unsafeRun(Traversable[List].foranyPar(as)(f.map {
case Some(x) => IO.succeedNow(x)
case None => IO.fail(())
}(Invariant.Function1Covariant)).option)
val expected : Option[Set[Int]] = {
val allResults = as.map(f)
if(allResults.nonEmpty && allResults.forall(_.isEmpty))
None
else
Some(allResults.flatten.toSet)
}
(actual, expected) match {
case (None , None) => assertCompletes
case (Some(Some(result)), Some(allPossible)) => assert(List(result))(hasOneOf(allPossible))
case (Some(None), Some(allPossible)) => assert(allPossible)(isEmpty)
}
}
},
testM("isEmpty") {
check(genList) { (as) =>
val actual = Traversable[List].isEmpty(as)
Expand Down

0 comments on commit 7d1cae7

Please sign in to comment.