Using a mutli parameter function as generic type of another function scala

Viewed 54

I have the following code

object Test {

  def bar: String => Double = {
    foo[String](_.toDouble)
  }

  def baz: (Double, Double) => Double = {
    foo[(Double, Double)] {
      case (d1, d2) => d1 + d2
    }
  }

  def foo[T](f: T => Double): T => Double = {
    f
  }
}

bar works with no trouble, as expected. I am trying to get a similar thing working with mutli parameter function as one of the inputs, but this doesn't work, because scala sees the foo[(Double, Double)] as a tuple type rather than as a function parameter. Is there any way to tell scala this is a function parameter, rather than a tuple?

the code: https://scastie.scala-lang.org/sCYyU6ziT3mKOhkBiofAaQ

1 Answers

I think the reason this didn't work for you is that you don't access the two separate arguments to the baz method, which need to be packaged into a tuple before calling foo. (I think there's some discussion about being able to convert parameter lists to/from tuples, but that's not currently possible.) Try this definition instead:

object Test {
  def bar: String => Double = {
    foo[String](_.toDouble)
  }
  def baz: (Double, Double) => Double = {(p1, p2) =>
    foo[(Double, Double)] {
      case(d1, d2) => d1 + d2
    }((p1, p2))
  }
  def foo[T](f: T => Double): T => Double = f
}

If I understand your intent correctly, this would be used like this (in the Scala REPL):

scala> Test.bar("5.0")
val res0: Double = 5.0
                                                                                                                                    
scala> Test.baz(3.0, 5.0)
val res1: Double = 8.0

However, this seems a little muddled. If bar and baz store method references, then they should be declared val, instead of def:

object Test {
  val bar: String => Double = {
    foo[String](_.toDouble)
  }
  val baz: (Double, Double) => Double = {(p1, p2) =>
    foo[(Double, Double)] {
      case(d1, d2) => d1 + d2
    }((p1, p2))
  }
  def foo[T](f: T => Double): T => Double = f
}

Which has the same usage syntax and produces the same output:

scala> Test.bar("5.0")
val res2: Double = 5.0
                                                                                                                                    
scala> Test.baz(3.0, 5.0)
val res3: Double = 8.0

But even that is still fairly clumsy. bar is a reference to a function that takes a String and returns a Double, while baz is a reference to a function that takes two Doubles and returns a Double. So why not just make bar and baz methods in their own right, instead of method references? This would produce:

object Test {
  def bar(s: String): Double = foo[String](_.toDouble)(s)

  def baz(p1: Double, p2: Double): Double = {
    foo[(Double, Double)] {
      case(d1, d2) => d1 + d2
    }((p1, p2))
  }

  def foo[T](f: T => Double): T => Double = f
}

which again is used in the same way:

scala> Test.bar("5.0")
val res4: Double = 5.0
                                                                                                                                    
scala> Test.baz(3.0, 5.0)
val res5: Double = 8.0

And, of course, foo itself is redundant, since it is identical to its argument. So this could all be written far more simply as just:

object Test {
  def bar(s: String): Double = s.toDouble

  def baz(p1: Double, p2: Double): Double = p1 + p2
}
Related