Scala 中的柯里化:函数中的多个参数,包括类型为 (=> A) 的匿名函数
Currying in Scala: Multiple parameters in a function including an anonymous function of type ( => A)
这两个代码块有什么区别?
def measure[A](histogram: Histogram)(thunk: ⇒ A): A = {
val start = RelativeNanoTimestamp.now
try thunk finally {
val latency = NanoInterval.since(start).nanos
histogram.record(latency)
}
def measure[A](histogram: Histogram, thunk: ⇒ A): A = {
val start = RelativeNanoTimestamp.now
try thunk finally {
val latency = NanoInterval.since(start).nanos
histogram.record(latency)
}
=> A
是惰性参数。在函数中引用时将对其进行评估。它可以是产生值的函数,也可以只是一个值。
您示例中的单个参数列表和多个参数列表之间的主要区别:
def measure[A](histogram: Histogram)(thunk: ⇒ A)
def measure[A](histogram: Histogram, thunk: ⇒ A)
(不考虑隐式和类型推断)是您应用函数的方式:
scala> def f[A](i: Int)(p: => A): A = { p }
f: [A](i: Int)(p: => A)A
scala> f(1)(2)
res0: Int = 2
scala> f(1){ println("something") }
something
scala> f(1){
| println("test")
| }
test
scala> def f2[A](i: Int, p: => A): A = { p }
f2: [A](i: Int, p: => A)A
scala> f2(1, 2)
res4: Int = 2
scala> f2(1, println("test"))
test
scala> f2(1, { println("test") })
test
看到带有多个参数列表的 f
允许我们以这种风格编写:f(...){...}
,而 f2
如果你有多行代码块作为第二个,那么 f2
有点不那么优雅参数:f(..., {...})
.
此外,如果你做了很多 currying/partial 应用程序,那么 f2
比 f
更容易处理:
scala> val f_withFirstArg = f(1) _
f_withFirstArg: (=> Nothing) => Nothing = <function1>
scala> val f2_withFirstArg = f2(1, _)
<console>:8: error: missing parameter type for expanded function ((x) => f2(1, x))
val f2_withFirstArg = f2(1, _)
^
我们必须明确指定参数类型,类型推断失败:
scala> val f2_withFirstArg = f2(1, _: String)
f2_withFirstArg: String => String = <function1>
如果您想了解它的技术,那么值得指出的是它们实际上属于不同的类型:
scala> :type f _
Int => ((=> Nothing) => Nothing)
scala> :type f2 _
(Int, => Nothing) => Nothing
f
是一个接受 Int
的函数,而 returns 是另一个接受类型 A
并将生成类型 A
的函数。 f2
是一个接受 2 个参数的函数:Int
和 A
和 returns A
.
这真的取决于你的代码。如果您需要做大量的部分应用程序或由于类型推断缺陷而需要较少的注释,那么请使用多个参数列表。否则没有必要使事情过于复杂并使用常规的单参数列表函数。
最后,只要有意义,您总是可以从一种类型的函数转换为另一种类型的函数:
scala> f2 _
res13: (Int, => Nothing) => Nothing = <function2>
scala> f2 _ curried
warning: there were 1 feature warning(s); re-run with -feature for details
res14: Int => ((=> Nothing) => Nothing) = <function1>
scala> f _ curried
<console>:9: error: value curried is not a member of Int => ((=> Nothing) => Nothing)
f _ curried
^
scala> f _ tupled
<console>:9: error: value tupled is not a member of Int => ((=> Nothing) => Nothing)
f _ tupled
^
scala> f2 _ tupled
warning: there were 1 feature warning(s); re-run with -feature for details
res17: ((Int, => Nothing)) => Nothing = <function1>
请注意,我们不能使 f
咖喱化,因为它已经是。我们不能使 f
变成元组,因为它不会改变任何东西。但是,我们可以使用 curried
:
将 f2
转换为 f
scala> :type f _
Int => ((=> Nothing) => Nothing)
scala> :type f2 _ curried _
Int => ((=> Nothing) => Nothing)
这两个代码块有什么区别?
def measure[A](histogram: Histogram)(thunk: ⇒ A): A = {
val start = RelativeNanoTimestamp.now
try thunk finally {
val latency = NanoInterval.since(start).nanos
histogram.record(latency)
}
def measure[A](histogram: Histogram, thunk: ⇒ A): A = {
val start = RelativeNanoTimestamp.now
try thunk finally {
val latency = NanoInterval.since(start).nanos
histogram.record(latency)
}
=> A
是惰性参数。在函数中引用时将对其进行评估。它可以是产生值的函数,也可以只是一个值。
您示例中的单个参数列表和多个参数列表之间的主要区别:
def measure[A](histogram: Histogram)(thunk: ⇒ A)
def measure[A](histogram: Histogram, thunk: ⇒ A)
(不考虑隐式和类型推断)是您应用函数的方式:
scala> def f[A](i: Int)(p: => A): A = { p }
f: [A](i: Int)(p: => A)A
scala> f(1)(2)
res0: Int = 2
scala> f(1){ println("something") }
something
scala> f(1){
| println("test")
| }
test
scala> def f2[A](i: Int, p: => A): A = { p }
f2: [A](i: Int, p: => A)A
scala> f2(1, 2)
res4: Int = 2
scala> f2(1, println("test"))
test
scala> f2(1, { println("test") })
test
看到带有多个参数列表的 f
允许我们以这种风格编写:f(...){...}
,而 f2
如果你有多行代码块作为第二个,那么 f2
有点不那么优雅参数:f(..., {...})
.
此外,如果你做了很多 currying/partial 应用程序,那么 f2
比 f
更容易处理:
scala> val f_withFirstArg = f(1) _
f_withFirstArg: (=> Nothing) => Nothing = <function1>
scala> val f2_withFirstArg = f2(1, _)
<console>:8: error: missing parameter type for expanded function ((x) => f2(1, x))
val f2_withFirstArg = f2(1, _)
^
我们必须明确指定参数类型,类型推断失败:
scala> val f2_withFirstArg = f2(1, _: String)
f2_withFirstArg: String => String = <function1>
如果您想了解它的技术,那么值得指出的是它们实际上属于不同的类型:
scala> :type f _
Int => ((=> Nothing) => Nothing)
scala> :type f2 _
(Int, => Nothing) => Nothing
f
是一个接受 Int
的函数,而 returns 是另一个接受类型 A
并将生成类型 A
的函数。 f2
是一个接受 2 个参数的函数:Int
和 A
和 returns A
.
这真的取决于你的代码。如果您需要做大量的部分应用程序或由于类型推断缺陷而需要较少的注释,那么请使用多个参数列表。否则没有必要使事情过于复杂并使用常规的单参数列表函数。
最后,只要有意义,您总是可以从一种类型的函数转换为另一种类型的函数:
scala> f2 _
res13: (Int, => Nothing) => Nothing = <function2>
scala> f2 _ curried
warning: there were 1 feature warning(s); re-run with -feature for details
res14: Int => ((=> Nothing) => Nothing) = <function1>
scala> f _ curried
<console>:9: error: value curried is not a member of Int => ((=> Nothing) => Nothing)
f _ curried
^
scala> f _ tupled
<console>:9: error: value tupled is not a member of Int => ((=> Nothing) => Nothing)
f _ tupled
^
scala> f2 _ tupled
warning: there were 1 feature warning(s); re-run with -feature for details
res17: ((Int, => Nothing)) => Nothing = <function1>
请注意,我们不能使 f
咖喱化,因为它已经是。我们不能使 f
变成元组,因为它不会改变任何东西。但是,我们可以使用 curried
:
f2
转换为 f
scala> :type f _
Int => ((=> Nothing) => Nothing)
scala> :type f2 _ curried _
Int => ((=> Nothing) => Nothing)