Tjunkie
Tjunkie

Reputation: 497

Scala: Linux like pipe with error handling using Either

I am relatively new to Scala.

Attempting to develop a pattern that would allow "concatenative" programming. The goal is to be able to chain operations on data from left to right in a linux "pipe" like fashion, while dragging along any error that may be returned from one of the methods. So that at the end of the chain there is an Either.

I was unable to boil it down to a single operator - I seem to have to differentiate depending on the method to the right of the operator - if it returns a plain result or one already wrapped in Either.

Here asking for critique / hints / pointers to existing library that does this

Thanks

    type PResult[R] = Either[String, R]

    implicit class fromVal[A](val in: A) {
        def |>[B](f: A => B): PResult[B] = Right(f(in))
        def |>>[B](f: A => PResult[B]): PResult[B] = f(in)
    }

    implicit class fromEither[A](val in: PResult[A]) {
        def |>[B](f: A => B): PResult[B] =
            in match {
                case Left(l) => Left(l)
                case Right(r) => Right(f(r))
            }
        def |>>[B](f: A => PResult[B]): PResult[B] =
            in match {
                case Left(l) => Left(l)
                case Right(r) => f(r)
            }
    }

    val f1: Int => Int = _ + 2                    //> f1  : Int => Int = <function1>
    val f2: Int => PResult[Int] = { in => Right(in + 3) }
                                                  //> f2  : Int => piper.PResult[Int] = <function1>

    22 |>> f2 |> f1 |>> f2                        //> res0: piper.PResult[Int] = Right(30)

I think that something similar can be used with futures and other things where a result could be success or failure.

I do realize that my |> and |>> are very similar to map and flatMap.

And if I compare the thing that I am trying to do with for-comprehensions - they are also not homogeneous - where you have to use <- for things that are wrapped in option or something and = where it is just a computation. So what did I miss, what could be improved ?

Upvotes: 1

Views: 242

Answers (1)

huynhjl
huynhjl

Reputation: 41646

I think you can rewrite the second implicit like this:

implicit class fromEither[A](val in: Either[String, A]) {
  def |>[B](f: A => B) = in.right.map(f)
  def |>>[B](f: A => Either[String, B]) = in.right.flatMap(f)
}

You can also make it extend AnyVal which should be slightly more performant. I would not even define the first implicit class. It is not much trouble to have to wrap the first element in Right, just like you do cat somefile.

Note that there is actually a unix pipe-like API for running processes http://www.scala-lang.org/api/current/#scala.sys.process.package.

If you want to take it to the next level, you can look at iteratees and enumeratees. See http://www.playframework.com/documentation/2.1.x/Enumeratees. It allows you to do something like this strings |>> toInt &>> sum where strings enumerates some strings, toInt convert each one of them to ints and sum adds them together.

Upvotes: 1

Related Questions