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

collect and mapMaybe for MonadFilter? #842

Closed
sir-wabbit opened this issue Jan 31, 2016 · 4 comments
Closed

collect and mapMaybe for MonadFilter? #842

sir-wabbit opened this issue Jan 31, 2016 · 4 comments

Comments

@sir-wabbit
Copy link

Along the lines of:

  implicit class MonadFilterOps[F[_], A](val fa: F[A]) extends AnyVal {
    def collect[B](pf: PartialFunction[A, B])(implicit F: MonadFilter[F]): F[B] =
      F.map(F.filter[A](fa)(pf.isDefinedAt))(pf.apply)
    def mapMaybe[B](f: A => Option[B])(implicit F: MonadFilter[F]): F[B] =
      F.flatMap(fa) { a => f(a) match {
          case Some(x) => F.pure(x)
          case None => F.empty
        }
      }
  }

I am not sure about the names (I took collect from the standard Scala collection library and mapMaybe from Haskell's Data.Maybe).

Some random thoughts:

  • Having a method that combines filter and map could be beneficial performance-wise (an implementation for List could do both in a single fold).
  • Concise implementation of List[Xor[A, B]] => List[B] (or some other MonadFilter instance, since List already has collect):
list.collect { case Xor.Right(x) => x }
list.mapMaybe { case Xor.Right(x) => Some(x); case Xor.Left(_) => None }
  • mapMaybe might not seem much better than using flatMap with pure and empty.
  • But more familiar and compatible with pre-existing A => Option[B] functions.
@sir-wabbit sir-wabbit changed the title collect and mapMaybe for MonadFilter? collect and mapMaybe for MonadFilter? Jan 31, 2016
@ceedubs
Copy link
Contributor

ceedubs commented Feb 2, 2016

@alexknvl thank you for the detailed suggestion. I agree that some things along these lines could be useful.

Some half-baked thoughts that come to mind as I ponder this:

  • Something similar to collect but with slightly different constraints could exist. You could define it on Foldable and require that you collect into a G[_] type that has a MonadCombine instance. This alternative approach might have a more performant default implementation but it might not lead to efficient overrides. I think both could likely exist.
  • My first reaction to mapMaybe was that the Option seems overly specific. But I guess you either need to either do this, map to F[B] instead of Option[B] and use flatMap (which can already be done), or put this on MonadCombine where I think you could generalize to F[B] for any Foldable F. This use-case might be common enough that it's worth supporting in the way that you've written it.
  • We may want to call it mapOption or something else instead of mapMaybe :)

@sir-wabbit
Copy link
Author

@ceedubs

Something similar to collect but with slightly different constraints could exist.

Indeed:

def partialFoldMap[F[_], A, B](fa: F[A])(pf: PartialFunction[A, B])(implicit F: Foldable[F], B: Monoid): B = 
  F.foldLeft(fa, B.empty)((b, a) => B.combine(b, if (pf.isDefinedAt(a)) pf(a) else B.empty))

And then to do List[Xor.Right[A, B]] => List[B]:

list.partialFoldMap { case Xor.Right(x) => List(x) }

@ceedubs
Copy link
Contributor

ceedubs commented Feb 3, 2016

@alexknvl I like your partialFoldMap idea. I think a slight optimization might be: F.foldLeft(fa, B.empty)((b, a) => if (pf.isDefinedAt(a)) B.combine(b, pf(a)) else b))

@ceedubs
Copy link
Contributor

ceedubs commented Jun 1, 2017

@alexknvl I'm going to go ahead and close this out, because I think that it was resolved by #1225. Please feel free to reopen or open a separate issue if there is anything that wasn't resolved by that PR.

@ceedubs ceedubs closed this as completed Jun 1, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants