dcastro
dcastro

Reputation: 68750

K / Kestrel Combinator for monads?

implicit class KComb[A](a: A) {
  def K(f: A => Any): A = { f(a); a }
}

Given this implementation of the K combinator, we can chain method calls on a value while applying side effects, without the need for temp variables. E.g.:

case class Document()
case class Result()

def getDocument: Document = ???
def print(d: Document): Unit = ???
def process(d: Document): Result = ???

val result = process(getDocument.K(print))
// Or, using the thrush combinator
// val result = getDocument |> (_.K(print)) |> process

Now, I need to do something similar, but using the IO monad instead.

def getDocument: IO[Document] = ???
def print(d: Document): IO[Unit] = ???
def process(d: Document): IO[Result] = ???

My question is: does a combinator for this operation exist already? Is there anything in Scalaz, or maybe some other library, that does this?

I couldn't find anything, so I rolled out this variant of the K combinator for monads myself. I called it tapM because 1) the K combinator is called tap in Ruby and unsafeTap in Scalaz and 2) it seems Scalaz follows the pattern of appending M to monadic variants of well known methods (e.g. foldLeftM, foldMapM, ifM, untilM, whileM).

But I'd still like to know if anything of the sort exists already and I'm just reinventing the wheel.

implicit class KMonad[M[_]: Monad, A](ma: M[A]) {

  def tapM[B](f: A => M[B]): M[A] =
    for {
      a <- ma
      _ <- f(a)
    } yield a
}

// usage
getDocument tapM print flatMap process

Upvotes: 5

Views: 714

Answers (1)

Mark Canlas
Mark Canlas

Reputation: 9583

Edit: My initial answer was misguided. Here's the correct one.

There's a flatTap method on FlatMap in cats and >>! on BindOps in scalaz.

getDocument flatTap print >>= process

getDocument >>! print >>= process

Edit^2: Changed flatMap to >>= to more easily show the relationship between tap and bind.

Upvotes: 2

Related Questions