Aandal
Aandal

Reputation: 51

Error in using composed function in further function composition

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

Upvotes: 0

Views: 122

Answers (1)

jwvh
jwvh

Reputation: 51271

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

Upvotes: 3

Related Questions