Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

pipeline operator in Scala

Tags:

r

scala

I want to know if it's possible to define a pipeline operator like %>% in magrittr package of R language. I found several similar implementations that look like:

implicit class PipelineContainer[F](val value: F) {
  def |>[G] (f: F => G) = f(value)
}

so that x |> f |> g works like g(f(x))

Now I want this operator works even when functions takes more than 1 parameter, in this case value on left side of pipeline parameter becomes the first parameter of function on the right side. For example, x |> f(2) |> g(3) becomes g(f(x, 2), 3). How can I implement this in scala? It doesn't have to be the same syntax as I show here, but the simpler the better.

like image 918
Bamqf Avatar asked Jan 07 '16 02:01

Bamqf


1 Answers

There are a few options.

One is to just create the functions inline. It's only slightly messy.

x |> (z=>f(z,2)) |> (z=>g(z,3))

Another is to create shortcut methods that can collapse the arity of an existing function. In general it's a lot of boilerplate, but the first one is easy enough:

implicit class RichPipes[Y](y: Y) {
  def |>[Z](f: Y => Z) = f(y)
  def &>[X, Z](f: (X, Y) => Z): (X => Z) = (x: X) => f(x, y)
}

Then you can inject the other arguments inline (taking advantage of the fact that & is higher precedence than |):

x |> 2 &> f |> 3 &> g

Personally, I find this style confusing, but the compiler's fine with it.

Another option is to convert the methods to functions to start with (if necessary) and then enrich those functions to have a partial application helper method:

implicit class RichFunction2[A,B,Z](f: (A,B) => Z) {
  def %(b: B): (A => Z) = (a: A) => f(a,b)
}

Now you can

x |> f _ % 2 |> g _ % 3

Finally, if you happen to be able to write the functions differently, you can make it work without any extra machinery save a trailing _ to let the compiler know what you're up to; the difference is just that the application goes through the last of multiple parameter blocks:

def h(y: Int)(x: Int) = x + y
def i(y: Int)(x: Int) = x * y

x |> h(2) _ |> i(3) _

As a final thought, though, injecting parameters mid-stream may not be the easiest thing to follow for the uninitiated. You might consider whether it's good programming practice as opposed to adopting a different workflow.

like image 53
Rex Kerr Avatar answered Oct 07 '22 10:10

Rex Kerr