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

У меня есть код на 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)
  }
}

person jmount    schedule 16.07.2009    source источник
comment
Вы хотите, чтобы аргумент z был параметризован?   -  person Daniel C. Sobral    schedule 16.07.2009
comment
Да, я хотел бы, чтобы аргумент z() был параметризованной функцией (например, моей f), а затем z() связывал параметр типа с двумя разными значениями.   -  person jmount    schedule 17.07.2009
comment
То, что было бы совершенно потрясающим, было бы какой-то функцией, которая принимала бы параметризованную функцию f и возвращала бы класс с одним членом, установленным в f[Int], а другим — в f[Double].   -  person jmount    schedule 17.07.2009
comment
Проблема решена! Спасибо вам всем. Это мой первый вопрос Stack-Overflow, и я поражен тем, насколько все вежливы и щедры.   -  person jmount    schedule 17.07.2009
comment
Итак, очень грубо (работая с этим интересным комментарием: stackoverflow.com/questions/818996/ ) кто-то, кто пришел из мира C++/Java, должен помнить: шаблоны C++ больше похожи на макросы (если они компилируются после подстановки, это хорошо), дженерики Java Основанные на стирании и дженерики Scala зависят от формирования пригодного для использования определения типа после подстановки? Интересные отличия.   -  person jmount    schedule 17.07.2009


Ответы (3)


Вы можете сделать это следующим образом:

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})
person Alexey Romanov    schedule 16.07.2009
comment
Это кажется разумным (используя объектное представление функций), но я (пока) недостаточно знаю о Scala, чтобы заставить его работать. В каждом случае, если ввести «z (f)», я получаю ошибки типа компиляции: отсутствует аргумент для метода f; полиморфное выражение не может быть создано с ожидаемым типом; - person jmount; 17.07.2009
comment
Это отлично! Очевидно, то, что вы сказали, имело смысл, я просто не знал достаточно, чтобы применить это. Строка, которую я хотел, это def z(u : {def f[Z] : Z=›Z}) = (u.f[Int], u.f[Double]), которая отлично работает. Философский зуд, который я хотел удовлетворить, заключался в том, чтобы написать «f» только один раз (гарантируя, что обе специализации являются одной и той же функцией), добавив несколько дополнительных объявлений, чтобы добиться этого. Спасибо. - person jmount; 17.07.2009

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

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

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

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

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

Как показывает ответ 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)})
  }
}
person James Iry    schedule 17.07.2009
comment
Спасибо, Джеймс, я посмотрю на это. У меня определенно нет претензий к системе типов Scala — я готов работать с ней, чтобы получить то, что хочу. И мне нравится, что компилятор сразу говорит мне, что не будет работать. Мое намерение таково, как вы сказали: исходная (неупрощенная) проблема - это функция, которая вычисляет различные алгебраические поля (некоторые дорогие для работы, некоторые дешевые для работы), и я хотел, чтобы Scala генерировала две специализированные функции с безопасным типом из шаблона начальная функция. Все остальные движущиеся части было легко организовать, за исключением последнего преобразования типа. - person jmount; 17.07.2009
comment
Синтаксис new ForAll лучше, чем у меня был. Я забыл, что это работает. - person Alexey Romanov; 17.07.2009
comment
Ссылка на Википедию: en.wikipedia.org/wiki/ - person CW Holeman II; 09.04.2012

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

Изменить:

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

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 нет системы макросов.

person Daniel C. Sobral    schedule 16.07.2009
comment
Мне нравится часть, где Scala выводит типы (это приятно). Спасибо за помощь в изучении области решений. - person jmount; 17.07.2009