views:

755

answers:

3

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)
  }
}
+2  A: 

I don't think what you want to do is possible.

Edit:

My previous version was flawed. This does work:

scala> def z(f: Int => Int, g: Double => Double) = w(f, g)
z: (f: (Int) => Int,g: (Double) => Double)Unit

scala> z(f, f)

But, of course, it is pretty much what you have.

I do not think it is even possible for it to work, because type parameters exist only at compile-time. At run time there is no such thing. So it doesn't make even sense to me to pass a parameterized function, instead of a function with the type parameters inferred by Scala.

And, no, Scala has no macro system.

Daniel
I like the part where Scala infers the types (that is nice). Thanks for helping explore the solution space.
jmount
+5  A: 

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})
Alexey Romanov
This seems sensible (using the object representation of functions), but I don't (yet) know enough about Scala to make it work. In each case if type in 'z(f)' I get the compile-type errors:missing argument for method f;polymorphic expression cannot be instantiated with expected type;
jmount
I've added a usage example.
Alexey Romanov
That is great! Obviously what you said made sense, I just didn't know enough to apply it. The line I wanted is def z(u : {def f[Z] : Z=>Z}) = (u.f[Int], u.f[Double]) which works perfectly. The philosophic itch I wanted to satisfy was writing 'f' only once (guarantying both specializations are the same function), adding some additional declarations to achieve that is not problem at all.Thank you.
jmount
+5  A: 

If you're curious, what you're talking about here is called "rank-k polymorphism." See wikipedia. In your case, k = 2. Some translating:

When you write

f[X](x : X) : X = ...

then you're saying that f has type "forall X.X -> X"

What you want for z is type "(forall Z.Z -> Z) -> Unit". That extra pair of parenthesis is a big difference. In terms of the wikipedia article it puts the forall qualifier before 2 arrows instead of just 1. The type variable can't be instantiated just once and carried through, it potentially has to be instantiated to many different types. (Here "instantiation" doesn't mean object construction, it means assigning a type to a type variable for type checking).

As alexy_r's answer shows this is encodable in Scala using objects rather than straight function types, essentially using classes/traits as the parens. Although he seems to have left you hanging a bit in terms of plugging it into your original code, so here it is:

// this is your code

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

// This is new code

    trait ForAll {
      def g[X](x : X) : X
    }

    def z(forall : ForAll) = w(forall.g[Int], forall.g[Double])
    z(new ForAll{def g[X](x : X) = f(x)})
  }
}
James Iry
Thanks, James I will look into that. I definitely have no complaint about the Scala type system- I am willing to work with it to get what I want. And I love that the compiler immediately tells me what will not work. My intent is as you said- the original (unsimplified) problem is a function that computes over different algebraic fields (some expensive to work with, some cheap to work with) and I wanted Scala to generate two specialized type-safe functions from a templated initial function. All the other moving parts were easy to arrange- except this last type transformation.
jmount
`new ForAll` syntax is nicer than what I had. I forgot that it works.
Alexey Romanov