如何组成一个零参数函数?
How to compose a zero-argument function?
我想实现的是将2个函数(其中一个是无参数函数)合二为一。
这里有一个例子可以让你了解我在做什么:
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
}
编译错误:
[error] found : () => Int
[error] required: ? => Int
[error] val zeroAndThenInt: () => String = intArgFunc compose zeroArgFunc
[error] ^
[error] one error found
知道哪里出了问题吗?
[UPD] Scala 版本是 2.13.1(如果重要的话)。
脱糖 () => 10
我们有
new Function0[Int] { def apply() = 10 }
和Function0
没有compose
或andThen
方法
trait Function0[... +R] extends ... { ...
def apply(): R
override def toString(): String = "<function0>"
}
所以好像Function0
是编不出来的。
另一方面,(i: Int) => s"hello $i"
和 (s: String) => println(s)
对应于 Function1
,它确实定义了 compose
方法,因此它们可以组合。
考虑将 () => 10
更改为 (_: Unit) => 10
,这会将类型从 Function0
更改为 Function1
,然后
(intArgFunc compose zeroArgFunc)()
输出res4: String = hello 10
.
针对@Duelist 的评论,恕我直言 Function0[T]
在语义上不等同于 Function1[Unit, T]
。例如,给定
val f = () => 10
val g = (_: Unit) => 10
然后
f()
g()
确实输出
res7: Int = 10
res8: Int = 10
然而
f(println("woohoo")) // error: no arguments allowed for nullary method apply
g(println("woohoo")) // OK!
我们看到两者没有相同的行为。尽管如此,如果您想将它们视为等效的,也许您可以在 Function0
上定义一个扩展方法并明确转换,例如
implicit class Fun0ToFun1[A, B](f: () => A) {
def toFun1: Unit => A = (_: Unit) => f()
}
将允许以下语法
(intArgFunc compose zeroArgFunc.toFun1)()
解决 @egordoe 的评论,开箱即用的 compose
只为 Function1
定义,因此 Function2
、Function3
等, 就像 Function0
一样组成。然而,我们可以在函数上定义扩展 composeN
方法,例如,假设我们想将 Function1
与 Function0
组合,然后
implicit class ComposeFun1WithFun0[A, B](f1: A => B) {
def compose0(f2: () => A): () => B = () => f1(f2())
}
给予
(intArgFunc compose0 zeroArgFunc)()
compose
on Function1
(intArgFunc
是)只是 defined 只接受单参数函数:
def compose[A](g: (A) => T1): (A) => R
您可以编写辅助函数来转换 () => A
to/from Unit => A
def toUnitFun[A](f: () => A): Unit => A = _ => f()
def fromUnitFun[A](f: Unit => A): () => A = () => f(())
然后
val zeroAndThenInt = fromUnitFun(intArgFunc compose toUnitFun(zeroArgFunc))
您甚至可以通过将 to/fromUnitFun
标记为 implicit
来让您的原始代码正常工作。
我想实现的是将2个函数(其中一个是无参数函数)合二为一。
这里有一个例子可以让你了解我在做什么:
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
}
编译错误:
[error] found : () => Int
[error] required: ? => Int
[error] val zeroAndThenInt: () => String = intArgFunc compose zeroArgFunc
[error] ^
[error] one error found
知道哪里出了问题吗?
[UPD] Scala 版本是 2.13.1(如果重要的话)。
脱糖 () => 10
我们有
new Function0[Int] { def apply() = 10 }
和Function0
没有compose
或andThen
方法
trait Function0[... +R] extends ... { ...
def apply(): R
override def toString(): String = "<function0>"
}
所以好像Function0
是编不出来的。
另一方面,(i: Int) => s"hello $i"
和 (s: String) => println(s)
对应于 Function1
,它确实定义了 compose
方法,因此它们可以组合。
考虑将 () => 10
更改为 (_: Unit) => 10
,这会将类型从 Function0
更改为 Function1
,然后
(intArgFunc compose zeroArgFunc)()
输出res4: String = hello 10
.
针对@Duelist 的评论,恕我直言 Function0[T]
在语义上不等同于 Function1[Unit, T]
。例如,给定
val f = () => 10
val g = (_: Unit) => 10
然后
f()
g()
确实输出
res7: Int = 10
res8: Int = 10
然而
f(println("woohoo")) // error: no arguments allowed for nullary method apply
g(println("woohoo")) // OK!
我们看到两者没有相同的行为。尽管如此,如果您想将它们视为等效的,也许您可以在 Function0
上定义一个扩展方法并明确转换,例如
implicit class Fun0ToFun1[A, B](f: () => A) {
def toFun1: Unit => A = (_: Unit) => f()
}
将允许以下语法
(intArgFunc compose zeroArgFunc.toFun1)()
解决 @egordoe 的评论,开箱即用的 compose
只为 Function1
定义,因此 Function2
、Function3
等, Function0
一样组成。然而,我们可以在函数上定义扩展 composeN
方法,例如,假设我们想将 Function1
与 Function0
组合,然后
implicit class ComposeFun1WithFun0[A, B](f1: A => B) {
def compose0(f2: () => A): () => B = () => f1(f2())
}
给予
(intArgFunc compose0 zeroArgFunc)()
compose
on Function1
(intArgFunc
是)只是 defined 只接受单参数函数:
def compose[A](g: (A) => T1): (A) => R
您可以编写辅助函数来转换 () => A
to/from Unit => A
def toUnitFun[A](f: () => A): Unit => A = _ => f()
def fromUnitFun[A](f: Unit => A): () => A = () => f(())
然后
val zeroAndThenInt = fromUnitFun(intArgFunc compose toUnitFun(zeroArgFunc))
您甚至可以通过将 to/fromUnitFun
标记为 implicit
来让您的原始代码正常工作。