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?
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.