Alvin Alexander has a very good explanation of how to integrate your custom monad into the Scala for comprehension including filtering. filtering … His example, just punts on withFilter
by suggestion to just call filter
within withFilter
. Can someone suggest a reference for explaining now to really implement withFilter
. I’m motivated because I have a use case I’m trying to implement. I.e., I have a monad for which filtering the entire future is a bad idea, and I’d love to be able to filter it lazily.
The right way to implement withFilter
is by creating a specialized class that suspends the actual filtering operation. Usually it comes down to implementing the following interface:
trait WithFilter[A, M[_]] {
def map[B](f: A => B): M[B]
def flatMap[B](f: A => M[B]): M[B]
def foreach[U](f: A => U): Unit
def withFilter(q: A => Boolean): WithFilter[A, M]
}
An implementation for Option
would look like this:
class OptionWF[A](self: Option[A], p: A => Boolean) extends WithFilter[A, Option] {
def map[B](f: A => B): Option[B] = self.filter(p).map(f)
def flatMap[B](f: A => Option[B]): Option[B] = self.filter(p).flatMap(f)
def foreach[U](f: A => U): Unit = self.filter(p).foreach(f)
def withFilter(q: A => Boolean): WithFilter[A, Option] = new OptionWF[A](self, a => p(a) && q(a))
}
Yes, that works. Thanks. In fact I want a monad wrapping a List[A] (rather than wrapping A) which implements (via map
/flatMap
) the maplist
/mapcon
protocol of Common Lisp. I don’t yet know whether it is possible, as the map
/flatMap
protocol does not provide me with a List[A]=>B
function. Nevertheless, it is a learning experience for me, and understanding whether or why it is impossible will be interesting.
@Jasper-M, ahh so the def withFilter
allows me to intersect the two predicates and the laziness implementation is handled by the class I inherited from? is that the gist?
The laziness is handled by the fact that both withFilter
in the Option
class and in our WithFilter
class should return an instance of WithFilter
instead of eagerly evaluating the filtering predicate. The predicate will not get evaluated until the next call to map
, flatMap
, or foreach
.