Say, I have the following object:
case class MyFancyObject(a: String, b: Int, c : Vector[String])
And what I needed is to get a single Vector[String]
containing all 'c's that match a given partial function.
E.g.:
val xs = Vector(
MyFancyObject("test1",1,Vector("test1-1","test1-2","test1-3")),
MyFancyObject("test2",2,Vector("test2-1","test2-2","test2-3")),
MyFancyObject("test3",3,Vector("test3-1","test3-2","test3-3")),
MyFancyObject("test4",4,Vector("test4-1","test4-2","test4-3"))
)
val partialFunction1 : PartialFunction[MyFancyObject,Vector[String]] = {
case MyFancyObject(_,b,c) if b > 2 => c
}
What I need to get is: Vector("test3-1","test3-2","test3-3","test4-1","test4-2","test4-3")
.
I solved this doing the following:
val res1 = xs.foldMap{
case MyFancyObject(_,b,c) if b > 2 => c
case _ => Vector.empty[String]
}
However, this made me curious. What I am doing here seemed to be a pretty common and natural thing: for each element of a foldable collection, try to apply a partial function and, should that fail, default to the Monoid
's empty
(Vector.empty
in my case). I searched in the library and I did not find anything doing this already, so I ended up adding this extension method in my code:
implicit class FoldableExt[F[_], A](foldable : F[A]) {
def foldCollect[B](pF: PartialFunction[A, B])(implicit F : Foldable[F], B : Monoid[B]) : B = {
F.foldMap(foldable)(pF.applyOrElse(_, (_ : A) => B.empty))
}
}
My question here is:
Is there any reason why such a method would not be in available already? Is it not a generic and common enough scenario, or am I missing something?
I think that if you really need the partial function, you don't want it to leak outside, because it's not very nice to use. The best thing to do, if you want to reuse your
partialFunction1
, is tolift
it to make it a total function that returnsOption
. Then you can provide your default case in the same place you use your partial function. Here's the approach:The
foldMap(partialFunction1.lift)
returnsSome(Vector(test3-1, test3-2, test3-3, test4-1, test4-2, test4-3))
. This is exactly what you have inres1
, but wrapped inOption
.