How to convert a generic method into generic function

157 Views Asked by At

Question

How to convert the method timed into a function?

val timing = new StringBuffer
def timed[T](label: String, code: => T): T = {
  val start = System.currentTimeMillis()
  val result = code
  val stop = System.currentTimeMillis()
  timing.append(s"Processing $label took ${stop - start} ms.\n")
  result
}

Below causes "error: not found: type T"

val timing = new StringBuffer
val timed: (String, => T) => T = (label, code) => {
    val start = System.currentTimeMillis()
    val result = code
    val stop = System.currentTimeMillis()
    timing.append(s"Processing $label took ${stop - start} ms.\n")
    result
}
2

There are 2 best solutions below

0
Dmytro Mitin On BEST ANSWER

There is no such thing as generic function in Scala (and generic value at all), only generic methods are.

Generic functions will appear in Scala 3.

https://github.com/lampepfl/dotty/pull/4672

http://dotty.epfl.ch/docs/reference/overview.html#new-constructs

val timing = new StringBuffer
val timed: [T] => (String, /*=>*/ T) => T = [T] => (label: String, code: /*=>*/ T) => {
  val start = System.currentTimeMillis()
  val result = code
  val stop = System.currentTimeMillis()
  timing.append(s"Processing $label took ${stop - start} ms.\n")
  result
}

in Dotty 0.20.0-RC1.

0
slouc On

Scala doesn't have polymorphic functions.

Let me use an example from an excellent blog post by Miles Sabin:

def singleton[T](t: T) = Set(t)

// eta-expanded to Int => Set[Int]
List(1, 2, 3) map singleton

// eta-expanded to String => Set[String]
List("foo", "bar", "baz") map singleton

Method singleton() gets eta-expanded into a function at the point of usage, and at that point its types are fixed into inferred concrete types. So it can easily "become" (be expanded into) a function Int => Set[Int] or String => Set[String], but cannot stay something like T => Set[T]. There's no built-in way of expressing polymorphic functions in the language itself (Scala 3 might change that).

However, there is a way to have polymorphic functions via natural transformations and/or shapeless's Poly with a bit of type gymnastics. Both are based on the same principle of encoding the function as a trait with higher kinded types for input/output and having a generic apply[T]() method with an independent type T which is fixed to a different concrete type every time the function is invoked (in our case those would be Int, String etc.).

Something like this:

trait PolymorphicFunction[F[_], G[_]] {
  def apply[T](f: F[T]): G[T]
}

But instead of attempting to explain it in details here, I would rather refer you to shapeless documentation and the aforementioned short series of blog posts.