Compose and andThen methods

I'm following the tutorial Pattern matching & functional composition on Scala compose and andThen methods. There's such an example:

scala> def addUmm(x: String) = x + " umm"
scala> def addAhem(x: String) = x + " ahem"

val ummThenAhem = addAhem(_).compose(addUmm(_))

When I try to use it I get an error:

<console>:7: error: missing parameter type for expanded function ((x$1) => addAhem(x$1).compose(((x$2) => addUmm(x$2))))
   val ummThenAhem = addAhem(_).compose(addUmm(_))
                             ^
<console>:7: error: missing parameter type for expanded function ((x$2) => addUmm(x$2))
   val ummThenAhem = addAhem(_).compose(addUmm(_))
                                               ^
<console>:7: error: type mismatch;
 found   : java.lang.String
 required: Int
     val ummThenAhem = addAhem(_).compose(addUmm(_))

However, this works:

val ummThenAhem = addAhem _ compose addUmm _

or even

val ummThenAhem = addAhem _ compose addUmm

What's wrong with the code in the tutorial? Isn't the latter expression the same as the first one without parenthesis?


Solution 1:

Well, this:

addUhum _

is an eta expansion. It converts methods into functions. On the other hand, this:

addUhum(_)

is an anonymous function. In fact, it is a partial function application, in that this parameter is not applied, and the whole thing converted into a function. It expands to:

x => addUhum(x)

The exact rules for expansion are a bit difficult to explain, but, basically, the function will "start" at the innermost expression delimiter. The exception is partial function applications, where the "x" is moved outside the function -- if _ is used in place of a parameter.

Anyway, this is how it expands:

val ummThenAhem = x => addAhem(x).compose(y => addUmm(y))

Alas, the type inferencer doesn't know the type of x or y. If you wish, you can see exactly what it tried using the parameter -Ytyper-debug.

Solution 2:

addAhem is a method. compose method is defined on functions. addAhem _ converts addAhem from method to function, so compose can be called on it. compose expects a function as it's argument. You are giving it a method addUmm by converting addUmm into a function with addUmm _ (The underscore can be left out because the compiler can automatically convert a method into a function when it knows that a function is expected anyway). So your code:

addAhem _ compose addUmm

is the same as

(addAhem _).compose(addUmm)

but not

addAhem(_).compose(addUmm(_))

PS I didn't look at the link you provided.

Solution 3:

From compose documentation:

Composes two instances of Function1 in a new Function1, with this function applied last.

so you should write

scala> val ummThenAhem = (addAhem _).compose(addUmm _)
ummThenAhem: String => java.lang.String = <function1>

to treat addAhem and addUmm as partially applied functions (i.e function1)

scala> addAhem _
res0: String => java.lang.String = <function1>

Solution 4:

I believe the tutorial was written for an earlier version of Scala (probably 2.7.7 or earlier). There have been some changes in the compiler since then, namely, extensions to the type system, which now cause the type inferencing to fail on the:

addUhum(_).compose(addAhem(_))

The lifting to a function still works with that syntax if you just write:

addUhum(_)