Reputation: 763
What is the difference between these two code blocks?
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)
}
Upvotes: 2
Views: 482
Reputation: 16412
=> A
is a lazy parameter. It will be evaluated when referred to in the function. It can be a function producing a value, or just a value.
The main difference between single and multiple parameter lists as in your example:
def measure[A](histogram: Histogram)(thunk: ⇒ A)
def measure[A](histogram: Histogram, thunk: ⇒ A)
(not considering implicits and type inference) is how you apply a function:
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
See that f
with multiple param lists allows us to write in this style: f(...){...}
, while f2
is a bit less elegant if you have multiline code block as a second argument: f(..., {...})
.
Furthermore if you do lot's of currying/partial application then f2
is a bit easier to deal with than 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$1) => f2(1, x$1))
val f2_withFirstArg = f2(1, _)
^
We have to specify parameter type explicitly, type inference fails short:
scala> val f2_withFirstArg = f2(1, _: String)
f2_withFirstArg: String => String = <function1>
If you want to get technical about it, then it's worth pointing out that they are actually of a different type:
scala> :type f _
Int => ((=> Nothing) => Nothing)
scala> :type f2 _
(Int, => Nothing) => Nothing
f
is a function that takes an Int
and returns another function that takes type A
and will produce type A
. f2
is a function that takes 2 args: Int
and A
and returns A
.
It really depends on your code. If you need to do lots of partial application or need less annotation due to type inference shortcomings then use multiple param lists. Otherwise there is no need to overcomplicate things and use regular single param list functions.
Finally you can always convert from one type of function to another as long as it makes sense:
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>
Notice that we can't make f
curried because it already is. We can't make f
tupled because it would not change anything. However, we can convert f2
into f
using curried
:
scala> :type f _
Int => ((=> Nothing) => Nothing)
scala> :type f2 _ curried _
Int => ((=> Nothing) => Nothing)
Upvotes: 3