Может ли Scala разрешать бесплатные параметры типа в аргументах (являются ли параметры типа Scala гражданами первого класса?)? - PullRequest
5 голосов
/ 16 июля 2009

У меня есть некоторый код Scala, который делает что-то изящное с двумя разными версиями параметризованной функции. Я значительно упростил это из своего приложения, но в конце мой код полон вызовов вида w(f[Int],f[Double]), где w() - мой магический метод. Я хотел бы иметь более магический метод, такой как z(f) = w(f[Int],f[Double]), - но я не могу заставить работать любой синтаксис, такой как z(f[Z]:Z->Z), так как мне кажется, что аргументы функций не могут иметь свои собственные параметры типа. Вот проблема как фрагмент кода Scala.

Есть идеи? Макрос мог бы сделать это, но я не думаю, что это часть 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)
  }
}

Ответы [ 3 ]

6 голосов
/ 16 июля 2009

Вы можете сделать это так:

trait Forall {
  def f[Z] : Z=>Z
}

def z(u : Forall) = w(u.f[Int], u.f[Double])

Или с использованием структурных типов:

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

Но это будет медленнее, чем в первой версии, поскольку она использует отражение.

РЕДАКТИРОВАТЬ: Вот как вы используете вторую версию:

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)

Для первой версии добавьте f1 extends Forall или просто

scala> z(new Forall{def f[Z] : Z=>Z = x => x})
6 голосов
/ 17 июля 2009

Если вам любопытно, то, о чем вы здесь говорите, называется «полиморфизмом ранга К». См. Википедию . В вашем случае k = 2. Некоторые переводят:

Когда вы пишете

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

тогда вы говорите, что f имеет тип "forall X.X -> X"

То, что вы хотите для z, это тип "(forall Z.Z -> Z) -> Unit". Та дополнительная пара скобок - большая разница. С точки зрения статьи в Википедии, он ставит квалификатор forall перед 2 стрелками, а не просто 1. Переменная типа не может быть создана только один раз и перенесена, ее потенциально нужно создать для множества различных типов. (Здесь «создание экземпляра» не означает конструирование объекта, это означает присвоение типа переменной типа для проверки типа).

Как показывает ответ alexy_r, это можно кодировать в 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

// Это новый код

    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)})
  }
}
2 голосов
/ 16 июля 2009

Я не думаю, что вы хотите сделать это возможно.

Edit:

Моя предыдущая версия была ошибочной. Это работает:

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

scala> z(f, f)

Но, конечно, это в значительной степени то, что у вас есть.

Я не думаю, что он даже может работать, потому что параметры типа существуют только во время компиляции. Во время выполнения нет такой вещи. Поэтому мне даже не имеет смысла передавать параметризованную функцию вместо функции с параметрами типа, определенными Scala.

И, нет, у Scala нет макросистемы.

...