Search code examples
scalatype-parameter

Can Scala allow free Type Parameters in arguments (are Scala Type Parameters first class citizens?)?


I have some Scala code that does something nifty with two different versions of a type-parameterized function. I have simplified this down a lot from my application but in the end my code full of calls of the form w(f[Int],f[Double]) where w() is my magic method. I would love to have a more magic method like z(f) = w(f[Int],f[Double]) - but I can't get any syntax like z(f[Z]:Z->Z) to work as it looks (to me) like function arguments can not have their own type parameters. Here is the problem as a Scala code snippet.

Any ideas? A macro could do it, but I don't think those are part of Scala.

object TypeExample {
  def main(args: Array[String]):Unit = {
    def f[X](x:X):X = x              // parameterize fn
    def v(f:Int=>Int):Unit = { }     // function that operates on an Int to Int function
    v(f)                             // applied, types correct
    v(f[Int])                        // appplied, types correct
    def w[Z](f:Z=>Z,g:Double=>Double):Unit = {} // function that operates on two functions
    w(f[Int],f[Double])              // works
// want something like this:  def z[Z](f[Z]:Z=>Z) = w(f[Int],f[Double])
// a type parameterized function that takes a single type-parameterized function as an  
// argument and then speicalizes the the argument-function to two different types,  
// i.e. a single-argument version of w() (or wrapper)
  }
}

Solution

  • You can do it like this:

    trait Forall {
      def f[Z] : Z=>Z
    }
    
    def z(u : Forall) = w(u.f[Int], u.f[Double])
    

    Or using structural types:

    def z(u : {def f[Z] : Z=>Z}) = w(u.f[Int], u.f[Double])
    

    But this will be slower than the first version, since it uses reflection.

    EDIT: This is how you use the second version:

    scala> object f1 {def f[Z] : Z=>Z = x => x}
    defined module f1
    
    scala> def z(u : {def f[Z] : Z=>Z}) = (u.f[Int](0), u.f[Double](0.0))
    z: (AnyRef{def f[Z]: (Z) => Z})(Int, Double)
    
    scala> z(f1)
    res0: (Int, Double) = (0,0.0)
    

    For the first version add f1 extends Forall or simply

    scala> z(new Forall{def f[Z] : Z=>Z = x => x})