简体   繁体   中英

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 ?

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM