0

I am having trouble understanding why I am unable to use a composed function and compose a new one. For eg: I have two functions f and g and I create a composed function composed1 from these. I tried to combine composed with fourth function lastOne and it fails.

I want to make two composed functions and for the second one Is there a way I can reuse the first composed function?

scala> def f(x: Int)(y: Int) = {
     | x + y
     | }
f: (x: Int)(y: Int)Int

scala> def g(a: Int)(b: Int) = {
     | a + b
     | }
g: (a: Int)(b: Int)Int

scala> def composed1(a: Int, b: Int) = {
     | f(a) _ andThen g(b)
     | }
composed1: (a: Int, b: Int)Int => Int

scala> composed1(2, 2)(5)
res1: Int = 9

scala> def lastOne(l: Int)(x: Int) = {
     | l + x
     | }
lastOne: (l: Int)(x: Int)Int

scala> def composed2(a: Int, b: Int, c: Int) = {
     | composed1(a, b) _ andThen lastOne(c)
     | }
<console>:14: error: _ must follow method; cannot follow Int => Int
       composed1(a, b) _ andThen lastOne(c)
                ^
<console>:14: error: missing argument list for method lastOne
Unapplied methods are only converted to functions when a function type is expected.
You can make this conversion explicit by writing `lastOne _` or `lastOne(_)(_)` instead of `lastOne`.
       composed1(a, b) _ andThen lastOne(c)

When I use all of them together it works

scala> def test(x: Int, y: Int, z: Int) = {
     | f(x) _ andThen g(y) _ andThen lastOne(z)
     | }
test: (x: Int, y: Int, z: Int)Int => Int

scala> test(2, 2, 4)(5)
res9: Int = 13

Aandal
  • 51
  • 2
  • 11

1 Answers1

3

f()() and g()(), as you define them, are methods. Methods are not functions but methods can be promoted to functions via "eta expansion". One way to do that is using the underscore in place of a passed parameter.

andThen() is a method on the Function trait that takes a function as an argument and returns a new function. It looks as if you can also use a method as the passed argument but it is silently being promoted to Function status.

So composed1() looks like a method but it is actually a Function, because that's what andThen() returns, and you can't apply the underscore eta expansion to a Function. It only works on methods.

As an experiment, turn f()() into a Function that does the same thing...

def f :Int => Int => Int = (x: Int) => (y: Int) => x + y

...now composed1() won't compile.

So, now that we know that composed1() is a Function, how do we get what we want from composed2()? Simple. Skip the underscore.

def composed2(a: Int, b: Int, c: Int) =
  composed1(a, b) andThen lastOne(c)

composed2(2, 2, 4)(5)   //res0: Int = 13
jwvh
  • 50,871
  • 7
  • 38
  • 64
  • Thanks for the explanation. I have accepted the answer. Quick question, In many places I see def is used to create a method and `val` is used to create a function, in your example you have used `def` to create a function. Is it this symbol `=>` that makes your definition a function instead of method? – Aandal May 14 '19 at 04:37
  • @Aandal; Defining a function using `val` is more usual, and makes more sense, than using `def`. I put it together rather quickly. In answer to your question, yes, the `=>` is a big clue that we're dealing with a function. Put the `f` method in the REPL and it will respond with `f: (x: Int)(y: Int)Int`. If you then ask the REPL `:type f` you'll get an error. The function `f`, on the other hand, is clearly type `f: Int => (Int => Int)` and the REPL will gladly tell you so. – jwvh May 14 '19 at 06:07