Search code examples
scalafunctional-programmingscalazscala-cats

Is there something like Collectable typeclass?


I noticed that containers like Option, OptionT, fs2.Stream have the following method:

def collect[B](f: PartialFunction[A, B])(implicit F: Functor[F]): OptionT[F, B] =
    OptionT(F.map(value)(_.collect(f)))

So I'm curious if there is something like Collectable typeclass:

trait Collectable[F[_]] {
  def collect[A, B](fa: F[A])(pf: PartialFunction[A, B]): F[B]
}

It seems quite useful to me and is pretty straightforward to implement, but anyway is there something with similar functionality included in cats/scalaz already?


Solution

  • The Foldable typeclass seems to come pretty close to what you want. It has a method collectFirst, with the following signature:

      def collectFirst[A, B](fa: F[A])(pf: PartialFunction[A, B]): Option[B] =
    

    and a curious implementation:

        foldRight(fa, Eval.now(Option.empty[B])) { (a, lb) =>
          // trick from TraversableOnce, 
          // used to avoid calling both isDefined and apply (or calling lift)
          val x = pf.applyOrElse(a, sentinel)
          if (x.asInstanceOf[AnyRef] ne sentinel) Eval.now(Some(x.asInstanceOf[B]))
          else lb
        }.value
    

    where sentinel is a strange little function that always returns itself:

    private val sentinel: Function1[Any, Any] = 
      new scala.runtime.AbstractFunction1[Any, Any]{ 
        def apply(a: Any) = this 
      }
    

    Those two last code snippets are supposed to provide some motivation to not reimplement collectFirst multiple times.