What I want to achieve is the 2 functions (one of them is no arg function) are composed into one.
Here is an example to give you idea what I am doing:
object Test extends App {
val zeroArgFunc = () => 10
val intArgFunc = (i: Int) => s"hello $i"
val stringArgFunc = (s: String) => println(s)
// This line works perfectly fine.
val intAndThenString: Int => Unit = stringArgFunc compose intArgFunc
// But this line fails with 'type mismatch' compilation error.
val zeroAndThenInt: () => String = intArgFunc compose zeroArgFunc
}
Compilation error:
[error] found : () => Int
[error] required: ? => Int
[error] val zeroAndThenInt: () => String = intArgFunc compose zeroArgFunc
[error] ^
[error] one error found
Any idea what's wrong?
[UPD] The Scala version is 2.13.1 (if it matters).
Desugaring () => 10
we have
new Function0[Int] { def apply() = 10 }
and Function0
does not have compose
or andThen
methods
trait Function0[... +R] extends ... { ...
def apply(): R
override def toString(): String = "<function0>"
}
so it seems Function0
cannot be composed.
On the other hand (i: Int) => s"hello $i"
and (s: String) => println(s)
correspond to Function1
which does have compose
method defined, hence they can be composed.
Consider changing () => 10
to (_: Unit) => 10
which changes the type from Function0
to Function1
, and then
(intArgFunc compose zeroArgFunc)()
outputs res4: String = hello 10
.
Addressing comment by @Duelist, IMHO Function0[T]
is not semantically equivalent to Function1[Unit, T]
. For example, given
val f = () => 10
val g = (_: Unit) => 10
then
f()
g()
indeed outputs
res7: Int = 10
res8: Int = 10
however
f(println("woohoo")) // error: no arguments allowed for nullary method apply
g(println("woohoo")) // OK!
where we see the two do not have the same behaviour. Nevertheless, if you would like to consider them as equivalent perhaps you could define an extension method on Function0
and be explicit about conversion, for example
implicit class Fun0ToFun1[A, B](f: () => A) {
def toFun1: Unit => A = (_: Unit) => f()
}
would allow the following syntax
(intArgFunc compose zeroArgFunc.toFun1)()
Addressing comment by @egordoe, out-of-the-box compose
is only ever defined for Function1
, thus Function2
, Function3
, etc., cannot be composed just like Function0
. However we could define extension composeN
methods on function, for example, say we want to compose Function1
with Function0
, then
implicit class ComposeFun1WithFun0[A, B](f1: A => B) {
def compose0(f2: () => A): () => B = () => f1(f2())
}
gives
(intArgFunc compose0 zeroArgFunc)()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With