2
def weirdfunc(message: String, f: (Int, Int) => Int){
            println(message + s" ${f(3,5)}")
          }

I have the function as above. How do I make it so that the function f is generic for all Numeric types?

1
  • 2
    def weirdfunc[T : Numeric](message: String, f: (T, T) => T) Commented Jun 29, 2017 at 20:19

3 Answers 3

4

What you want is called a higher-ranked type (specifically, rank 2). Haskell has support for these types, and Scala gets a lot of its type theory ideas from Haskell, but Scala has yet to directly support this particular feature.

Now, the thing is, with a bit of black magic, we can get Scala to do what you want, but the syntax is... not pretty. In Scala, functions are always monomorphic, but you want to pass a polymorphic function around as an argument. We can't do that, but we can pass a polymorphic function-like object around that looks and behaves mostly like a function. What would this object look like?

trait NumFunc {
  def apply[A : Numeric](a: A, b: A): A
}

It's just a trait that defines a polymorphic apply. Now we can define the function that you really want.

def weirdfunc(message: String, f: NumFunc) = ???

The trouble here, as I mentioned, is that the syntax is really quite atrocious. To call this, we can't just pass in a function anymore. We have to create a NumFunc and pass that in. Essentially, from a type theoretic perspective, we have to prove to the compiler that our function works for all numeric A. For instance, to call the simple weirdfunc that only takes integers and pass the addition function is very simple.

weirdfunc("Some message", (_ + _))

However, to call our "special" weirdfunc that works for all number types, we have to write this mess.

weirdfunc("Hi", new NumFunc {
  override def apply[A : Numeric](a: A, b: A): A = {
    import math.Numeric.Implicits._
    a + b
  }
})

And we can't hide that away with an implicit conversion because, as I alluded to earlier, functions are monomorphic, so any conversion coming out a function type is going to be monomorphic.

Bottom line. Is it possible? Yes. Is it worth the costs in terms of readability and usability? Probably not.

Sign up to request clarification or add additional context in comments.

Comments

2

Scala has a typeclass for this, so it's quite easy to achieve using a context bound and the standard lib.

def weirdfunc[T: Numeric](message: String, x: T, y: T, f: (T, T) => T) {
  println(message + s" ${f(x, y)}")
}

def test[T](a: T, b: T)(implicit ev: Numeric[T]): T = ev.plus(a, b)

weirdFunc[Int]("The sum is ", 3, 5, test)
// The sum is 8

Comments

2

Sorry cktang you cannot generify this. The caller gets to set the generic parameter.. not the called function.. just like the caller passes function parameters.
However you can use currying so that you pass the 'f' of type Int once, and then pass different Int pairs. Then you may pass 'f' of type Double, and pass different Double pairs.

  def weirdfunc[A](message: String, f: (A, A) => A)(x: A, y: A){
    println(message + s" ${f(x, y)}")
  }

  def g(x: Int, y: Int): Int = x * y
  val wierdfuncWithF = weirdfunc("hello", g) _
  wierdfuncWithF(3, 5)
  wierdfuncWithF(2, 3)

In particular what you want cannot be done as it will break generics rules.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.