Reputation: 497
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
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