diff --git a/alleycats-core/src/main/scala/alleycats/std/all.scala b/alleycats-core/src/main/scala/alleycats/std/all.scala index 9905da41e2..a0f63e9584 100644 --- a/alleycats-core/src/main/scala/alleycats/std/all.scala +++ b/alleycats-core/src/main/scala/alleycats/std/all.scala @@ -3,12 +3,15 @@ package std import export._ -@reexports( - EmptyKInstances, - ListInstances, - OptionInstances, - SetInstances, - TryInstances, - IterableInstances, - FutureInstances -) object all extends LegacySetInstances with LegacyTryInstances with LegacyIterableInstances with MapInstances +@reexports(EmptyKInstances, + ListInstances, + OptionInstances, + SetInstances, + TryInstances, + IterableInstances, + FutureInstances) object all + extends LegacySetInstances + with LegacySetInstancesBinCompat0 + with LegacyTryInstances + with LegacyIterableInstances + with MapInstances diff --git a/alleycats-core/src/main/scala/alleycats/std/set.scala b/alleycats-core/src/main/scala/alleycats/std/set.scala index 3477d946ed..6d790952a3 100644 --- a/alleycats-core/src/main/scala/alleycats/std/set.scala +++ b/alleycats-core/src/main/scala/alleycats/std/set.scala @@ -1,6 +1,6 @@ package alleycats.std -import cats.{Applicative, Eval, Foldable, Monad, Monoid, Traverse} +import cats.{Applicative, Eval, Foldable, Monad, Monoid, Traverse, TraverseFilter} import export._ import scala.annotation.tailrec @@ -116,10 +116,24 @@ object SetInstances { override def collectFirstSome[A, B](fa: Set[A])(f: A => Option[B]): Option[B] = fa.collectFirst(Function.unlift(f)) } + + @export(Orphan) + implicit val setTraverseFilter: TraverseFilter[Set] = + new TraverseFilter[Set] { + val traverse: Traverse[Set] = setTraverse + + def traverseFilter[G[_], A, B](fa: Set[A])(f: A => G[Option[B]])(implicit G: Applicative[G]): G[Set[B]] = + fa.foldLeft(G.pure(Set.empty[B])) { (gSet, a) => + G.map2(f(a), gSet) { + case (Some(b), set) => set + b + case (None, set) => set + } + } + } } @reexports(SetInstances) -object set extends LegacySetInstances +object set extends LegacySetInstances with LegacySetInstancesBinCompat0 // TODO: remove when cats.{ Set, Traverse } support export-hook trait LegacySetInstances { @@ -127,3 +141,7 @@ trait LegacySetInstances { implicit def legacySetTraverse(implicit e: ExportOrphan[Traverse[Set]]): Traverse[Set] = e.instance } + +trait LegacySetInstancesBinCompat0 { + implicit def legacySetTraverseFilter(implicit e: ExportOrphan[TraverseFilter[Set]]): TraverseFilter[Set] = e.instance +} diff --git a/alleycats-tests/src/test/scala/alleycats/tests/SetSuite.scala b/alleycats-tests/src/test/scala/alleycats/tests/SetSuite.scala index 02ae856d55..f9872c5ae4 100644 --- a/alleycats-tests/src/test/scala/alleycats/tests/SetSuite.scala +++ b/alleycats-tests/src/test/scala/alleycats/tests/SetSuite.scala @@ -3,6 +3,8 @@ package alleycats.tests import alleycats.laws.discipline._ import cats.Foldable import cats.kernel.laws.discipline.SerializableTests +import cats.laws.discipline.arbitrary._ +import cats.laws.discipline.TraverseFilterTests import alleycats.std.all._ @@ -10,4 +12,6 @@ class SetSuite extends AlleycatsSuite { checkAll("FlatMapRec[Set]", FlatMapRecTests[Set].tailRecM[Int]) checkAll("Foldable[Set]", SerializableTests.serializable(Foldable[Set])) + + checkAll("TraverseFilter[Set]", TraverseFilterTests[Set].traverseFilter[Int, Int, Int]) }