Skip to content

Commit

Permalink
Add TraverseFilter instance for Set to alleycats (#2726)
Browse files Browse the repository at this point in the history
* Add `TraverseFilter` instance for `Set` to alleycats

* Add `TraverseFilter` instance for `Map` to alleycats
  • Loading branch information
dcastro authored and kailuowang committed Feb 27, 2019
1 parent c4ffc11 commit 8b69af2
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 13 deletions.
22 changes: 13 additions & 9 deletions alleycats-core/src/main/scala/alleycats/std/all.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@ 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
with MapInstancesBinCompat0
20 changes: 19 additions & 1 deletion alleycats-core/src/main/scala/alleycats/std/map.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package std

import cats._

object map extends MapInstances
object map extends MapInstances with MapInstancesBinCompat0

trait MapInstances {

Expand Down Expand Up @@ -59,3 +59,21 @@ trait MapInstances {
collectFirst(fa)(Function.unlift(f))
}
}

trait MapInstancesBinCompat0 extends MapInstances {
implicit def mapTraverseFilter[K]: TraverseFilter[Map[K, ?]] =
new TraverseFilter[Map[K, ?]] {
def traverse: Traverse[Map[K, ?]] = alleycatsStdInstancesForMap

def traverseFilter[G[_], A, B](fa: Map[K, A])(f: A => G[Option[B]])(implicit G: Applicative[G]): G[Map[K, B]] = {
val gba: Eval[G[Map[K, B]]] = Always(G.pure(Map.empty))
Foldable
.iterateRight(fa, gba) { (kv, lbuf) =>
G.map2Eval(f(kv._2), lbuf)({ (ob, buf) =>
ob.fold(buf)(b => buf + (kv._1 -> b))
})
}
.value
}
}
}
22 changes: 20 additions & 2 deletions alleycats-core/src/main/scala/alleycats/std/set.scala
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -116,14 +116,32 @@ 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 {
implicit def legacySetMonad(implicit e: ExportOrphan[Monad[Set]]): Monad[Set] = e.instance

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
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package alleycats.tests

import cats.laws.discipline.SerializableTests
import cats.laws.discipline.arbitrary._
import cats.laws.discipline.{SerializableTests, TraverseFilterTests}
import cats.Traverse
import alleycats.std.all._

class MapSuite extends AlleycatsSuite {
checkAll("Traverse[Map[Int, ?]]", SerializableTests.serializable(Traverse[Map[Int, ?]]))

checkAll("TraverseFilter[Map[Int, ?]]", TraverseFilterTests[Map[Int, ?]].traverseFilter[Int, Int, Int])
}
4 changes: 4 additions & 0 deletions alleycats-tests/src/test/scala/alleycats/tests/SetSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@ 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._

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])
}

0 comments on commit 8b69af2

Please sign in to comment.