Как объявить абстрактную функцию с переменным аргументом в Scala

#scala

#scala

Вопрос:

Я пытаюсь объявить функцию в черте, которая принимает переменное количество аргументов, и во время реализации черты я бы расширил количество аргументов. Как это можно сделать в Scala

Я ожидаю получить код, подобный приведенному ниже.

 trait Column {
  def rule
}

case object FirstColumn extends Column{
 def rule(s: String) : String
}

case object SecondColumn extends Column{
 def rule(s1: String, s2: String) : String
}
  

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

Комментарии:

1. Что за «вышеуказанная подпись» ? У вас есть три метода с тремя разными сигнатурами.

2. Если вы объявляете rule , что принимаете переменные аргументы в чертеже, то вы обещаете, что реализация работает для любого числа аргументов, а не только для некоторого определенного количества аргументов, зависящего от реализации. Таким образом, FirstColumn который предоставляет одноразовую версию функции, неправильно реализует признак.

3. Другими словами, если у меня есть Column , как бы я вызвал rule ее? Если я передаю один аргумент, и он оказывается SecondColumn , это ошибка. И наоборот, если я передаю два, и это FirstColumn . Итак, если у вас есть Column , то вы не можете сделать с ней ничего типобезопасного, что сводит на нет смысл того, что это вообще черта.

4. Спасибо Сильвио.. Я думаю, что type safe решит мою проблему.. Я больше думал о том, чтобы иметь что-то вроде «любой класс, который реализует этот признак, всегда должен иметь функцию с именем «rule» и позволять пользователю указывать аргументы, которые они желают», но это противоречит цели trait, и я никогда не смогу вызвать его с помощью «Column».. Спасибо за комментарий

5. Спасибо Луису.. Это было понятно..

Ответ №1:

Это в первую очередь расширяет мой комментарий к вопросу. Этот ответ приближает вас настолько, насколько Scala позволяет вам приблизиться к тому, что вы хотите, но он также показывает, почему, вероятно, не очень хорошая идея делать то, что вы делаете.

Вы можете выразить (что-то близкое к) желаемый тип, но я не уверен, что вы намерены получить. Во-первых, если вы хотите использовать разные типы списков аргументов, то Column он должен быть универсальным.

 trait Column[-A] {
  def rule(arg: A): String
}
  

Затем мы можем реализовать ваши объекты case как подклассы соответствующей параметризации этого.

 case object FirstColumn extends Column[String] {
  def rule(arg: String): String =
    "stub implementation"
}

case object SecondColumn extends Column[(String, String)] {
  def rule(arg: (String, String)): String =
    "stub implementation"
}
  

Обратите внимание, что FirstColumn и SecondColumn не наследуются от одного и того же Column[A] , поскольку они не реализуют один и тот же метод. Мы можем заставить их иметь общий тип, но… не очень полезным способом.

Один из вариантов — найти общий супертип Column[String] and Column[(String, String)] , который (поскольку аргумент контравариантен) сродни поиску общего подтипа String and (String, String) . Ближайший распространенный подтип — это … Null . Это бесполезно, если вы только не планируете передавать null в свой rule .

Вместо этого мы можем использовать existentials.

 val foo: Column[_] = FirstColumn
val bar: Column[_] = SecondColumn
  

Теперь мы потеряли всю информацию о типе. Вы можете получить доступ к слоту foo.rule и распечатать его, но вы не можете вызвать его, потому что мы не знаем, что нам нужно передать. Вам нужно будет выполнить приведение, чтобы вернуть ее к удобному формату.

Смысл, который я здесь подчеркиваю, заключается в том, что да, это выполнимо, но как только вы потеряете столько информации о типе, сколько отдаете, в этом не будет особого смысла. Система типов правильно сообщает нам, что foo и bar практически не имеют ничего общего, кроме существования метода с именем rule , который принимает … какой-то аргумент. С точки зрения теории типов, трудно придумать что-то более неинтересное, чем это.

Комментарии:

1. Спасибо Сильвио.. Это помогает.. Моим вариантом использования было перебрать все объекты case и получить метод run и передать его как функцию высокого порядка в Spark UDF. Мое намерение состоит в том, чтобы создать универсальную функцию, которая может принимать n количество аргументов и возвращать строку.. Я передам эту функцию как функцию HO. Пожалуйста, дайте мне знать, если у вас есть какие-либо мысли по этому поводу, и ваш ответ был очень полезен для меня.. Спасибо..

2. @NareshKumarSundaramKrishnam Итак, по сути, у вас есть DF с неизвестным количеством строк-столбцов, и вы хотите выполнить над ним какую-то операцию, ориентированную на строки?