Filter vs withFilter

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.