6

Expressions like

ls map (_ + 1) sum

are lovely because they are left-to-right and not nested. But if the functions in question are defined outside the class, it is less pretty.

Following an example I tried

final class DoublePlus(val self: Double) {
    def hypot(x: Double) = sqrt(self*self + x*x)
}

implicit def doubleToDoublePlus(x: Double) =
    new DoublePlus(x)

which works fine as far as I can tell, other than

  1. A lot of typing for one method

  2. You need to know in advance that you want to use it this way

Is there a trick that will solve those two problems?

Owen
  • 38,836
  • 14
  • 95
  • 125
  • What is wrong with defining hypot in a utility object and then use as `hypot(3,4)`? Seems like the best choice here. – IttayD Aug 29 '11 at 10:31

5 Answers5

13

You can call andThen on a function object:

(h andThen g andThen f)(x)

You can't call it on methods directly though, so maybe your h needs to become (h _) to transform the method into a partially applied function. The compiler will translate subsequent method names to functions automatically because the andThen method accepts a Function parameter.

You could also use the pipe operator |> to write something like this:

x |> h |> g |> f
Community
  • 1
  • 1
Jean-Philippe Pellet
  • 59,296
  • 21
  • 173
  • 234
2

Enriching an existing class/interface with an implicit conversion (which is what you did with doubleToDoublePlus) is all about API design when some classes aren't under your control. I don't recommend to do that lightly just to save a few keystrokes or having a few less parenthesis. So if it's important to be able to type val h = d hypot x, then the extra keystrokes should not be a concern. (there may be object allocations concerns but that's different).

The title and your example also don't match:

f(g(h(x))) can be rewritten asf _ compose g _ compose h _ apply x if your concern is about parenthesis or f compose g compose h apply x if f, g, h are function objects rather than def.

But ls map (_ + 1) sum aren't nested calls as you say, so I'm not sure how that relates to the title. And although it's lovely to use, the library/language designers went through a lot of efforts to make it easy to use and under the hood is not simple (much more complex than your hypot example).

huynhjl
  • 41,520
  • 14
  • 105
  • 158
  • Well they *are* nested calls in a sense: the result of each step becomes the `this` argument to the next call. In other languages this nesting would be explicit. My issue is that while whether a particular argument is a `this` argument is functionally not very significant, it is only because of this fact that you get nice calling syntax. – Owen Aug 29 '11 at 07:51
2
def fgh (n: N) = f(g(h(n))) 
val m = fgh (n) 
user unknown
  • 35,537
  • 11
  • 75
  • 121
0

Starting Scala 2.13, the standard library provides the chaining operation pipe which can be used to convert/pipe a value with a function of interest.

Using multiple pipes we can thus build a pipeline which as mentioned in the title of your question, minimizes the number of parentheses:

import scala.util.chaining._

x pipe h pipe g pipe f
Xavier Guihot
  • 54,987
  • 21
  • 291
  • 190
0

Maybe this, observe how a is provided:

def compose[A, B, C](f: B => C, g: A => B): A => C = (a: A) => f(g(a))

basically like the answer above combine the desired functions to a intermediate one which you then can use easily with map.

AndreasScheinert
  • 1,918
  • 12
  • 18