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

Add FunctorFilter and TraverseFilter #1225

Merged
merged 8 commits into from
Jul 28, 2016
Merged
Show file tree
Hide file tree
Changes from 5 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
18 changes: 17 additions & 1 deletion core/src/main/scala/cats/Composed.scala
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,30 @@ private[cats] trait ComposedFoldable[F[_], G[_]] extends Foldable[λ[α => F[G[
F.foldRight(fga, lb)((ga, lb) => G.foldRight(ga, lb)(f))
}

private[cats] trait ComposedTraverse[F[_], G[_]] extends Traverse[λ[α => F[G[α]]]] with ComposedFoldable[F, G] with ComposedFunctor[F, G] { outer =>
private[cats] trait ComposedTraverse[F[_], G[_]] extends Traverse[λ[α => F[G[α]]]] with ComposedFoldable[F, G] with ComposedFunctor[F, G] {
def F: Traverse[F]
def G: Traverse[G]

override def traverse[H[_]: Applicative, A, B](fga: F[G[A]])(f: A => H[B]): H[F[G[B]]] =
F.traverse(fga)(ga => G.traverse(ga)(f))
}

private[cats] trait ComposedTraverseFilter[F[_], G[_]] extends TraverseFilter[λ[α => F[G[α]]]] with ComposedTraverse[F, G] {
def F: Traverse[F]
def G: TraverseFilter[G]

override def traverseFilter[H[_]: Applicative, A, B](fga: F[G[A]])(f: A => H[Option[B]]): H[F[G[B]]] =
F.traverse[H, G[A], G[B]](fga)(ga => G.traverseFilter(ga)(f))
}

private[cats] trait ComposedFunctorFilter[F[_], G[_]] extends FunctorFilter[λ[α => F[G[α]]]] with ComposedFunctor[F, G] {
def F: Functor[F]
def G: FunctorFilter[G]

override def mapFilter[A, B](fga: F[G[A]])(f: A => Option[B]): F[G[B]] =
F.map(fga)(G.mapFilter(_)(f))
}

private[cats] trait ComposedReducible[F[_], G[_]] extends Reducible[λ[α => F[G[α]]]] with ComposedFoldable[F, G] { outer =>
def F: Reducible[F]
def G: Reducible[G]
Expand Down
6 changes: 6 additions & 0 deletions core/src/main/scala/cats/Functor.scala
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ import simulacrum.typeclass
val G = Functor[G]
}

def composeFilter[G[_]: FunctorFilter]: FunctorFilter[λ[α => F[G[α]]]] =
new ComposedFunctorFilter[F, G] {
val F = self
val G = FunctorFilter[G]
}

override def composeContravariant[G[_]: Contravariant]: Contravariant[λ[α => F[G[α]]]] =
new ComposedCovariantContravariant[F, G] {
val F = self
Expand Down
62 changes: 62 additions & 0 deletions core/src/main/scala/cats/FunctorFilter.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package cats

import simulacrum.typeclass

@typeclass trait FunctorFilter[F[_]] extends Functor[F] {

/**
* A combined [[map]] and [[filter]]. Filtering is handled via `Option`
* instead of `Boolean` such that the output type `B` can be different than
* the input type `A`.
*
* Example:
* {{{
* scala> import cats.implicits._
* scala> val m: Map[Int, String] = Map(1 -> "one", 3 -> "three")
* scala> val l: List[Int] = List(1, 2, 3, 4)
* scala> def asString(i: Int): Option[String] = m.get(i)
* scala> l.mapFilter(i => m.get(i))
* res0: List[String] = List(one, three)
* }}}
*/
def mapFilter[A, B](fa: F[A])(f: A => Option[B]): F[B]
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder if it is worth thinking about a typeclass for mapConcat? The law would be similar to the one here. Collections support this but also distributed compute libraries like spark and scalding.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@johnynek what's mapConcat?

Copy link
Contributor

Choose a reason for hiding this comment

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

def mapConcat[A, B](fa: F[A])(f: A => Iterable[B]): F[B] something like that.

For instance there is no FunctorMapConcat[Option] but there is FunctorMapConcat[Vector] and in the case of scalding FunctorMapConcat[TypedPipe].

Copy link
Contributor

Choose a reason for hiding this comment

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

Actually, when you think about it, it might be something like this:

trait FunctorArrow[F[_], A[_, _]] extends Functor[F] {
  def arrow: Arrow[A]
  def mapArrow[T, U](f: F[T], arrow: A[T, U]): F[U]
}

And we are just talking about various KleisliArrows here.

Or maybe we mean something like:

trait FunctorTraverse[F[_], M[_]] extends Functor[F] {
  // this is similar to traverse, except we end up with `F[B]` not `M[F[B]]`
  def mapTraverse[A, B](fa: F[A])(f: A => M[B]): F[B]
}

It is not clear what the right level of generalization is. It seems to me A => M[B] where you have Foldable[M] is pretty generally useful. Maybe that is the thing you want instead of mapConcat:

trait FunctorFoldableFn[F[_]] extend Functor[F] {
  def mapFoldable[A, B, M[_]](f: F[A])(f: A => M[B])(implicit M: Foldable[M]): F[A]
}

Copy link
Contributor Author

@ceedubs ceedubs Jul 26, 2016

Choose a reason for hiding this comment

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

There was some similar discussion a long time ago here. I wonder what the valid laws for this construct would be and whether you would run into issues where just working with Foldable would mean implementations would tend to be inefficient.

@johnynek were you suggesting something like this instead of FunctorFilter or TraverseFilter? I would tend to think that something like this should be in addition to these type classes. As you've mentioned you couldn't have an instance of this type class for Option or OptionT, which I think is a pretty big limitation, since they are common types to call filter or collect on. I also feel like there is some benefit to knowing these methods can never make your structure grow (they can only reduce it or leave it the same); however this is just a fuzzy feeling and not something that I can confidently say is a useful property.

Copy link
Contributor

Choose a reason for hiding this comment

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

I was mostly thinking out loud to figure out what this should be.


/**
* Similar to [[mapFilter]] but uses a partial function instead of a function
* that returns an `Option`.
*
* Example:
* {{{
* scala> import cats.implicits._
* scala> val l: List[Int] = List(1, 2, 3, 4)
* scala> TraverseFilter[List].collect(l){
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this refer to FunctorFilter here instead of TraverseFilter as this is the docs for FunctorFilter.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for catching my cut/paste error :)

* | case 1 => "one"
* | case 3 => "three"
* | }
* res0: List[String] = List(one, three)
* }}}
*/
def collect[A, B](fa: F[A])(f: PartialFunction[A, B]): F[B] =
mapFilter(fa)(f.lift)

/**
* "Flatten" out a structure by collapsing `Option`s.
*
* Example:
* {{{
* scala> import cats.implicits._
* scala> val l: List[Option[Int]] = List(Some(1), None, Some(3), None)
* scala> l.flattenOption
* res0: List[Int] = List(1, 3)
* }}}
*/
def flattenOption[A](fa: F[Option[A]]): F[A] = mapFilter(fa)(identity)

/**
* Apply a filter to a structure such that the output structure contains all
* `A` elements in the input structure that satisfy the predicate `f` but none
* that don't.
*/
def filter[A](fa: F[A])(f: A => Boolean): F[A] =
mapFilter(fa)(a => if (f(a)) Some(a) else None)
}
8 changes: 4 additions & 4 deletions core/src/main/scala/cats/MonadFilter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ import simulacrum.typeclass

/**
* a Monad equipped with an additional method which allows us to
* create an "Empty" value for the Monad (for whatever "empty" makes
* create an "empty" value for the Monad (for whatever "empty" makes
* sense for that particular monad). This is of particular interest to
* us since it allows us to add a `filter` method to a Monad, which is
* used when pattern matching or using guards in for comprehensions.
*/
@typeclass trait MonadFilter[F[_]] extends Monad[F] {
@typeclass trait MonadFilter[F[_]] extends Monad[F] with FunctorFilter[F] {

def empty[A]: F[A]
Copy link
Contributor

Choose a reason for hiding this comment

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

It seems like a FunctorFilter had to have an empty value too. We could just filter everything from any value and get there, no?

Seems like empty might almost be there.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We could just filter everything from any value and get there, no?

That's true, but you would have to have a value to start with. With Applicative you have pure for that, but with Functor this isn't available.

I was thinking about whether the empty from MonadFilter should be moved to FunctorFilter along with the filter method at first, but I think this constrains FunctorFilter more than we might want. For example, that would mean that the FunctorFilter instance for OptionT[F, A] would then require an Applicative[F] as opposed to the Functor[F] that it currently requires. And more generally, I think that what I have in this PR as a composeFilter method on Functor would need to instead be moved to Applicative.

I think that if we were to add an ApplicativeFilter, that would be the place to add the def empty[A]: F[A]. With ApplicativeFilter, you could even implement empty and pure in terms of each other (with help from map).

At this point I haven't added ApplicativeFilter because I didn't have a particularly compelling use-case for it and there's already a lot going on in this type class hierarchy. However, I should be able to add it if people have good use-cases for it.


def filter[A](fa: F[A])(f: A => Boolean): F[A] =
flatMap(fa)(a => if (f(a)) pure(a) else empty[A])
override def mapFilter[A, B](fa: F[A])(f: A => Option[B]): F[B] =
flatMap(fa)(a => f(a).fold(empty[B])(pure))
}
6 changes: 6 additions & 0 deletions core/src/main/scala/cats/Traverse.scala
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,12 @@ import simulacrum.typeclass
val G = Traverse[G]
}

def composeFilter[G[_]: TraverseFilter]: TraverseFilter[λ[α => F[G[α]]]] =
new ComposedTraverseFilter[F, G] {
val F = self
val G = TraverseFilter[G]
}

override def map[A, B](fa: F[A])(f: A => B): F[B] =
traverse[Id, A, B](fa)(f)
}
65 changes: 65 additions & 0 deletions core/src/main/scala/cats/TraverseFilter.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package cats

import simulacrum.typeclass

/**
* `TraverseFilter`, also known as `Witherable`, represents list-like structures
* that can essentially have a [[traverse]] and a [[filter]] applied as a single
* combined operation ([[traverseFilter]]).
*
* Must obey the laws defined in cats.laws.TraverseFilterLaws.
*
* Based on Haskell's [[https://hackage.haskell.org/package/witherable-0.1.3.3/docs/Data-Witherable.html Data.Witherable]]
*/
@typeclass trait TraverseFilter[F[_]] extends Traverse[F] with FunctorFilter[F] { self =>

/**
* A combined [[traverse]] and [[filter]]. Filtering is handled via `Option`
* instead of `Boolean` such that the output type `B` can be different than
* the input type `A`.
*
* Example:
* {{{
* scala> import cats.implicits._
* scala> val m: Map[Int, String] = Map(1 -> "one", 3 -> "three")
* scala> val l: List[Int] = List(1, 2, 3, 4)
* scala> def asString(i: Int): Eval[Option[String]] = Now(m.get(i))
* scala> val result: Eval[List[String]] = l.traverseFilter(asString)
* scala> result.value
* res0: List[String] = List(one, three)
* }}}
*/
def traverseFilter[G[_]: Applicative, A, B](fa: F[A])(f: A => G[Option[B]]): G[F[B]]

override def mapFilter[A, B](fa: F[A])(f: A => Option[B]): F[B] =
traverseFilter[Id, A, B](fa)(f)

/**
*
* Filter values inside a `G` context.
*
* This is a generalized version of Haskell's [[http://hackage.haskell.org/package/base-4.9.0.0/docs/Control-Monad.html#v:filterM filterM]].
* [[http://stackoverflow.com/questions/28872396/haskells-filterm-with-filterm-x-true-false-1-2-3 This StackOverflow question]] about `filterM` may be helpful in understanding how it behaves.
*
* Example:
* {{{
* scala> import cats.implicits._
* scala> val l: List[Int] = List(1, 2, 3, 4)
* scala> def odd(i: Int): Eval[Boolean] = Now(i % 2 == 1)
* scala> val res: Eval[List[Int]] = l.filterA(odd)
* scala> res.value
* res0: List[Int] = List(1, 3)
*
* scala> List(1, 2, 3).filterA(_ => List(true, false))
* res1: List[List[Int]] = List(List(1, 2, 3), List(1, 2), List(1, 3), List(1), List(2, 3), List(2), List(3), List())
* }}}
*/
def filterA[G[_], A](fa: F[A])(f: A => G[Boolean])(implicit G: Applicative[G]): G[F[A]] =
traverseFilter(fa)(a => G.map(f(a))(if (_) Some(a) else None))

override def filter[A](fa: F[A])(f: A => Boolean): F[A] =
filterA[Id, A](fa)(f)

override def traverse[G[_], A, B](fa: F[A])(f: A => G[B])(implicit G: Applicative[G]): G[F[B]] =
traverseFilter(fa)(a => G.map(f(a))(Some(_)))
}
12 changes: 9 additions & 3 deletions core/src/main/scala/cats/data/Const.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ final case class Const[A, B](getConst: A) {
def combine(that: Const[A, B])(implicit A: Semigroup[A]): Const[A, B] =
Const(A.combine(getConst, that.getConst))

def traverseFilter[F[_], C](f: B => F[Option[C]])(implicit F: Applicative[F]): F[Const[A, C]] =
F.pure(retag[C])

def traverse[F[_], C](f: B => F[C])(implicit F: Applicative[F]): F[Const[A, C]] =
F.pure(retag[C])

Expand Down Expand Up @@ -53,13 +56,16 @@ private[data] sealed abstract class ConstInstances extends ConstInstances0 {
fa.retag[B]
}

implicit def catsDataTraverseForConst[C]: Traverse[Const[C, ?]] = new Traverse[Const[C, ?]] {
def traverse[G[_]: Applicative, A, B](fa: Const[C, A])(f: A => G[B]): G[Const[C, B]] =
fa.traverse(f)
implicit def catsDataTraverseFilterForConst[C]: TraverseFilter[Const[C, ?]] = new TraverseFilter[Const[C, ?]] {
def traverseFilter[G[_]: Applicative, A, B](fa: Const[C, A])(f: A => G[Option[B]]): G[Const[C, B]] =
fa.traverseFilter(f)

def foldLeft[A, B](fa: Const[C, A], b: B)(f: (B, A) => B): B = b

def foldRight[A, B](fa: Const[C, A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = lb

override def traverse[G[_]: Applicative, A, B](fa: Const[C, A])(f: A => G[B]): G[Const[C, B]] =
fa.traverse(f)
}

implicit def catsDataMonoidForConst[A: Monoid, B]: Monoid[Const[A, B]] = new Monoid[Const[A, B]]{
Expand Down
39 changes: 37 additions & 2 deletions core/src/main/scala/cats/data/Nested.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,18 @@ final case class Nested[F[_], G[_], A](value: F[G[A]])

object Nested extends NestedInstances

private[data] sealed abstract class NestedInstances extends NestedInstances1 {
private[data] sealed abstract class NestedInstances extends NestedInstances0 {
implicit def catsDataEqForNested[F[_], G[_], A](implicit FGA: Eq[F[G[A]]]): Eq[Nested[F, G, A]] =
FGA.on(_.value)

implicit def catsDataTraverseFilterForNested[F[_]: Traverse, G[_]: TraverseFilter]: TraverseFilter[Nested[F, G, ?]] =
new NestedTraverseFilter[F, G] {
val FG: TraverseFilter[λ[α => F[G[α]]]] = Traverse[F].composeFilter[G]
}
}

private[data] sealed abstract class NestedInstances0 extends NestedInstances1 {

implicit def catsDataTraverseForNested[F[_]: Traverse, G[_]: Traverse]: Traverse[Nested[F, G, ?]] =
new NestedTraverse[F, G] {
val FG: Traverse[λ[α => F[G[α]]]] = Traverse[F].compose[G]
Expand Down Expand Up @@ -118,7 +126,14 @@ private[data] sealed abstract class NestedInstances8 extends NestedInstances9 {
}
}

private[data] sealed abstract class NestedInstances9 {
private[data] sealed abstract class NestedInstances9 extends NestedInstances10 {
implicit def catsDataFunctorFilterForNested[F[_]: Functor, G[_]: FunctorFilter]: FunctorFilter[Nested[F, G, ?]] =
new NestedFunctorFilter[F, G] {
val FG: FunctorFilter[λ[α => F[G[α]]]] = Functor[F].composeFilter[G]
}
}

private[data] sealed abstract class NestedInstances10 {
implicit def catsDataInvariantForNestedContravariant[F[_]: Invariant, G[_]: Contravariant]: Invariant[Nested[F, G, ?]] =
new NestedInvariant[F, G] {
val FG: Invariant[λ[α => F[G[α]]]] = Invariant[F].composeContravariant[G]
Expand All @@ -139,6 +154,26 @@ private[data] trait NestedFunctor[F[_], G[_]] extends Functor[Nested[F, G, ?]] w
Nested(FG.map(fga.value)(f))
}

private[data] trait NestedFunctorFilter[F[_], G[_]] extends FunctorFilter[Nested[F, G, ?]] with NestedFunctor[F, G] {
override def FG: FunctorFilter[λ[α => F[G[α]]]]

override def mapFilter[A, B](fga: Nested[F, G, A])(f: A => Option[B]): Nested[F, G, B] =
Nested(FG.mapFilter(fga.value)(f))

override def collect[A, B](fga: Nested[F, G, A])(f: PartialFunction[A, B]): Nested[F, G, B] =
Nested(FG.collect(fga.value)(f))

override def filter[A](fga: Nested[F, G, A])(f: A => Boolean): Nested[F, G, A] =
Nested(FG.filter(fga.value)(f))
}

private[data] trait NestedTraverseFilter[F[_], G[_]] extends TraverseFilter[Nested[F, G, ?]] with NestedFunctorFilter[F, G] with NestedTraverse[F, G] {
override def FG: TraverseFilter[λ[α => F[G[α]]]]

override def traverseFilter[H[_]: Applicative, A, B](fga: Nested[F, G, A])(f: A => H[Option[B]]): H[Nested[F, G, B]] =
Applicative[H].map(FG.traverseFilter(fga.value)(f))(Nested(_))
}

private[data] trait NestedApply[F[_], G[_]] extends Apply[Nested[F, G, ?]] with NestedFunctor[F, G] {
override def FG: Apply[λ[α => F[G[α]]]]

Expand Down
22 changes: 17 additions & 5 deletions core/src/main/scala/cats/data/OptionT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ final case class OptionT[F[_], A](value: F[Option[A]]) {
def semiflatMap[B](f: A => F[B])(implicit F: Monad[F]): OptionT[F, B] =
flatMap(a => OptionT.liftF(f(a)))

def mapFilter[B](f: A => Option[B])(implicit F: Functor[F]): OptionT[F, B] =
OptionT(F.map(value)(_.flatMap(f)))

def flatMap[B](f: A => OptionT[F, B])(implicit F: Monad[F]): OptionT[F, B] =
flatMapF(a => f(a).value)

Expand Down Expand Up @@ -99,6 +102,9 @@ final case class OptionT[F[_], A](value: F[Option[A]]) {
def ===(that: OptionT[F, A])(implicit eq: Eq[F[Option[A]]]): Boolean =
eq.eqv(value, that.value)

def traverseFilter[G[_], B](f: A => G[Option[B]])(implicit F: Traverse[F], G: Applicative[G]): G[OptionT[F, B]] =
G.map(F.composeFilter(optionInstance).traverseFilter(value)(f))(OptionT.apply)

def traverse[G[_], B](f: A => G[B])(implicit F: Traverse[F], G: Applicative[G]): G[OptionT[F, B]] =
G.map(F.compose(optionInstance).traverse(value)(f))(OptionT.apply)

Expand Down Expand Up @@ -219,19 +225,22 @@ private[data] sealed trait OptionTInstances1 extends OptionTInstances2 {
}

private[data] sealed trait OptionTInstances2 extends OptionTInstances3 {
implicit def catsDataTraverseForOptionT[F[_]](implicit F0: Traverse[F]): Traverse[OptionT[F, ?]] =
new OptionTTraverse[F] { implicit val F = F0 }
implicit def catsDataTraverseForOptionT[F[_]](implicit F0: Traverse[F]): TraverseFilter[OptionT[F, ?]] =
new OptionTTraverseFilter[F] { implicit val F = F0 }
}

private[data] sealed trait OptionTInstances3 {
implicit def catsDataFunctorForOptionT[F[_]](implicit F0: Functor[F]): Functor[OptionT[F, ?]] =
implicit def catsDataFunctorFilterForOptionT[F[_]](implicit F0: Functor[F]): FunctorFilter[OptionT[F, ?]] =
new OptionTFunctor[F] { implicit val F = F0 }
}

private[data] trait OptionTFunctor[F[_]] extends Functor[OptionT[F, ?]] {
private[data] trait OptionTFunctor[F[_]] extends FunctorFilter[OptionT[F, ?]] {
implicit def F: Functor[F]

override def map[A, B](fa: OptionT[F, A])(f: A => B): OptionT[F, B] = fa.map(f)

override def mapFilter[A, B](fa: OptionT[F, A])(f: A => Option[B]): OptionT[F, B] =
fa.mapFilter(f)
}

private[data] trait OptionTMonad[F[_]] extends Monad[OptionT[F, ?]] {
Expand Down Expand Up @@ -273,9 +282,12 @@ private[data] trait OptionTFoldable[F[_]] extends Foldable[OptionT[F, ?]] {
fa.foldRight(lb)(f)
}

private[data] sealed trait OptionTTraverse[F[_]] extends Traverse[OptionT[F, ?]] with OptionTFoldable[F] {
private[data] sealed trait OptionTTraverseFilter[F[_]] extends TraverseFilter[OptionT[F, ?]] with OptionTFoldable[F] {
implicit def F: Traverse[F]

def traverseFilter[G[_]: Applicative, A, B](fa: OptionT[F, A])(f: A => G[Option[B]]): G[OptionT[F, B]] =
fa traverseFilter f

override def traverse[G[_]: Applicative, A, B](fa: OptionT[F, A])(f: A => G[B]): G[OptionT[F, B]] =
fa traverse f
}
Expand Down
13 changes: 10 additions & 3 deletions core/src/main/scala/cats/instances/list.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import cats.data.Xor

trait ListInstances extends cats.kernel.instances.ListInstances {

implicit val catsStdInstancesForList: Traverse[List] with MonadCombine[List] with MonadRec[List] with CoflatMap[List] =
new Traverse[List] with MonadCombine[List] with MonadRec[List] with CoflatMap[List] {
implicit val catsStdInstancesForList: TraverseFilter[List] with MonadCombine[List] with MonadRec[List] with CoflatMap[List] =
new TraverseFilter[List] with MonadCombine[List] with MonadRec[List] with CoflatMap[List] {

def empty[A]: List[A] = Nil

Expand Down Expand Up @@ -63,7 +63,12 @@ trait ListInstances extends cats.kernel.instances.ListInstances {
Eval.defer(loop(fa))
}

def traverse[G[_], A, B](fa: List[A])(f: A => G[B])(implicit G: Applicative[G]): G[List[B]] =
def traverseFilter[G[_], A, B](fa: List[A])(f: A => G[Option[B]])(implicit G: Applicative[G]): G[List[B]] =
foldRight[A, G[List[B]]](fa, Always(G.pure(List.empty))){ (a, lglb) =>
G.map2Eval(f(a), lglb)((ob, l) => ob.fold(l)(_ :: l))
}.value

override def traverse[G[_], A, B](fa: List[A])(f: A => G[B])(implicit G: Applicative[G]): G[List[B]] =
foldRight[A, G[List[B]]](fa, Always(G.pure(List.empty))){ (a, lglb) =>
G.map2Eval(f(a), lglb)(_ :: _)
}.value
Expand All @@ -75,6 +80,8 @@ trait ListInstances extends cats.kernel.instances.ListInstances {
fa.forall(p)

override def isEmpty[A](fa: List[A]): Boolean = fa.isEmpty

override def filter[A](fa: List[A])(f: A => Boolean): List[A] = fa.filter(f)
}

implicit def catsStdShowForList[A:Show]: Show[List[A]] =
Expand Down
Loading