Shehaaz
Shehaaz

Reputation: 763

Currying in Scala: Multiple parameters in a function including an anonymous function of type ( => A)

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)
}

Github Source

Upvotes: 2

Views: 482

Answers (1)

yǝsʞǝla
yǝsʞǝla

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

Related Questions