Как определить параметр функции по умолчанию, связанный с универсальным параметром?

Я пытаюсь реорганизовать функцию (найденную ближе к концу этого ответа StackOverflow), чтобы сделать ее немного более общей. Вот исходное определение функции:

def tryProcessSource(
  file: File,
  parseLine: (Int, String) => Option[List[String]] =
    (index, unparsedLine) => Some(List(unparsedLine)),
  filterLine: (Int, List[String]) => Option[Boolean] =
    (index, parsedValues) => Some(true),
  retainValues: (Int, List[String]) => Option[List[String]] =
    (index, parsedValues) => Some(parsedValues)
): Try[List[List[String]]] = {
  ???
}

И вот что я пытаюсь изменить:

def tryProcessSource[A <: Any](
  file: File,
  parseLine: (Int, String) => Option[List[String]] =
    (index, unparsedLine) => Some(List(unparsedLine)),
  filterLine: (Int, List[String]) => Option[Boolean] =
    (index, parsedValues) => Some(true),
  transformLine: (Int, List[String]) => Option[A] =
    (index, parsedValues) => Some(parsedValues)
): Try[List[A]] = {
  ???
}

Я продолжаю получать сообщение об ошибке выделения в редакторе IntelliJ на Some(parsedValues), в котором говорится: «Выражение типа Some[List[String]] не соответствует ожидаемому типу Option[A]». Мне не удалось понять, как правильно изменить определение функции, чтобы оно удовлетворяло требуемому условию; то есть ошибка уходит.

Если я изменю transformLine на это (заменю общий параметр A на Any)...

transformLine: (Int, List[String]) => Option[Any] =
  (index, parsedValues) => Some(parsedValues)

... ошибка исчезает. Но я также теряю строгую типизацию, связанную с общим параметром.

Любая помощь в этом очень ценится.


person chaotic3quilibrium    schedule 15.12.2015    source источник
comment
Хорошо, как вы можете преобразовать List[String] в произвольное A? На самом деле это не имеет смысла, если у вас уже нет доказательств для преобразования List[String] в A. Некоторый серьезный рефакторинг с использованием классов типов может сработать, но я не думаю, что имеет смысл больше иметь параметр по умолчанию.   -  person Michael Zajac    schedule 15.12.2015
comment
@ m-z Теперь я думаю, что понимаю, о чем ты говоришь. Пожалуйста, посмотрите ответ, который я только что опубликовал, и дайте мне знать, если вы заметили что-то не так или что-то можно улучшить. Спасибо.   -  person chaotic3quilibrium    schedule 15.12.2015


Ответы (2)


В transformLine: (Int, List[String]) => Option[A] = (index, parsedValues) => whatever тип parsedValues, очевидно, имеет тип List[String], поэтому Some(parsedValues) равен Some[List[String]]. Для transformLine просто нет разумного значения по умолчанию.

Вы можете изменить его тип на (Int, A) => Option[A] или (Int, List[A]) => Option[List[A]] в зависимости от того, что вам нужно, а также изменить предыдущие аргументы, но вы застрянете на том факте, что строки имеют Strings, поэтому вам придется преобразовать их в A где-то.

person Alexey Romanov    schedule 15.12.2015
comment
Я не понимаю вашего ответа. Смысл transformLine состоит в том, чтобы взять индекс и проанализированные строковые значения из определенной строки и преобразовать их в необязательный произвольный тип. Преобразование — это весь смысл функции transformLine. И поскольку функция может возвращать Option[+Any], почему Some[List[String]] не соответствует Option[+Any]. IOW, если я изменю его явно на Option[Any] (в отличие от варианта [A]), то ошибка исчезнет, ​​и Some(parsedValue) будет работать нормально. - person chaotic3quilibrium; 15.12.2015
comment
@ chaotic3quilibrium — ваш transformLine по умолчанию не преобразует строку в произвольный тип, он работает только для String, поэтому вы получаете сообщение об ошибке. Option[Any] отличается от Option[A], поскольку клиент выбирает A, а функция может легко выбрать Any. - person Lee; 15.12.2015
comment
@Алексей Романов Ладно, кажется, теперь я понимаю, о чем ты говоришь. Теперь я создал ответ на свой вопрос, который более явно и конкретно выражает то, что вы подразумеваете. - person chaotic3quilibrium; 15.12.2015
comment
@ chaotic3quilibrium Предположим, кто-то вызывает tryProcessSource[Int](file) (оставляя остальные аргументы по умолчанию). Тогда transformLine должен иметь тип (Int, List[String]) => Option[Int], которого нет в вашем значении по умолчанию. - person Alexey Romanov; 15.12.2015
comment
Теперь я думаю, что понял. Пожалуйста, прочитайте ответ, который я только что опубликовал, чтобы убедиться, что я действительно его понял. И я очень ценю любые отзывы, которые вы можете дать мне в отношении рекомендаций/принципов, которые я выделил. - person chaotic3quilibrium; 15.12.2015

Прочитав и перечитав ответ Алексея Ромонова и комментарий Ли, я понял, как реорганизовать это, чтобы заставить его работать. Кроме того, я думаю, что это также может подразумевать указание относительно предоставления значений по умолчанию для функции.

Если я правильно понимаю, я пытаюсь объединить конкретный, в этом примере случай List[String] в параметре преобразования по умолчанию, с общим типом, в данном случае Option[A]. По сути, это перекрывает границу, на которой компилятор создает универсальную копию функции для конкретного предоставленного пользователем типа и явно предоставленного List[String]. Это приводит меня к следующему правилу определения универсальных функций:

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


Итак, имея в виду это руководство, давайте переработаем исходный код...

Конкретная функция выглядит следующим образом (очень похожа на исходную предварительно обобщенную версию, которая также включала желаемые конкретные параметры по умолчанию):

def tryProcessSource(
  file: File,
  parseLine: (Int, String) => Option[List[String]] =
    (index, unparsedLine) => Some(List(unparsedLine)),
  filter: (Int, List[String]) => Option[Boolean] =
    (index, parsedValues) => Some(true),
  transform: (Int, List[String]) => Option[List[String]] =
    (index, parsedValues) => Some(parsedValues)
): Try[List[List[String]]] =
  tryProcessSourceGeneric(file, parseLine, filter, transform)

А обобщенная функция выглядит так (обратите внимание на отсутствие каких-либо значений параметров по умолчанию):

def tryProcessSourceGeneric[A, B](
  file: File,
  parseLine: (Int, String) => Option[A],
  filter: (Int, A) => Option[Boolean]
  transform: (Int, A) => Option[B]
): Try[List[B]] = {
  ???
}

Еще раз огромное спасибо Алексею и Ли за то, что помогли мне справиться с этим.

person chaotic3quilibrium    schedule 15.12.2015
comment
Может иметь смысл, чтобы параметры, включающие дженерики, имели значения по умолчанию. В вашем примере у вас может быть такое же значение по умолчанию для filterLine, потому что оно работает для любого A. Или, если вы удалите параметр B и получите transform: (Int, A) => Option[A], то (_, x) => Some(x) будет разумным значением по умолчанию. - person Alexey Romanov; 15.12.2015
comment
Потрясающий! Так что моя рекомендация верна для filter и моего существующего transform. Однако это не относится к вашему примеру transform. Хм... не знаю, как перефразировать. Обдумывая... - person chaotic3quilibrium; 15.12.2015