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(_)